diff --git a/.github/workflows/cmake-arm.yml b/.github/workflows/cmake-arm.yml index 4767ae3..59b6cd5 100644 --- a/.github/workflows/cmake-arm.yml +++ b/.github/workflows/cmake-arm.yml @@ -22,7 +22,7 @@ jobs: - name: Install bare metal ARM toolchain run: sudo apt-get install binutils-arm-none-eabi gcc-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib - + - name: Show toolchain version run: arm-none-eabi-gcc --version @@ -36,8 +36,8 @@ jobs: # access regardless of the host operating system shell: bash working-directory: ${{runner.workspace}}/build - # Note the current convention is to use the -S and -B options here to specify source - # and build directories, but this is only available with CMake 3.13 and higher. + # Note the current convention is to use the -S and -B options here to specify source + # and build directories, but this is only available with CMake 3.13 and higher. # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 run: cmake $GITHUB_WORKSPACE/examples/knx-cc1310 -DCMAKE_BUILD_TYPE=$BUILD_TYPE @@ -50,6 +50,6 @@ jobs: # - name: Test # working-directory: ${{runner.workspace}}/build # shell: bash - # Execute tests defined by the CMake configuration. + # Execute tests defined by the CMake configuration. # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail # run: ctest -C $BUILD_TYPE diff --git a/.github/workflows/cmake-native.yml b/.github/workflows/cmake-native.yml index 5629c97..5e835d1 100644 --- a/.github/workflows/cmake-native.yml +++ b/.github/workflows/cmake-native.yml @@ -27,8 +27,8 @@ jobs: # access regardless of the host operating system shell: bash working-directory: ${{runner.workspace}}/build - # Note the current convention is to use the -S and -B options here to specify source - # and build directories, but this is only available with CMake 3.13 and higher. + # Note the current convention is to use the -S and -B options here to specify source + # and build directories, but this is only available with CMake 3.13 and higher. # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE @@ -41,6 +41,6 @@ jobs: # - name: Test # working-directory: ${{runner.workspace}}/build # shell: bash - # Execute tests defined by the CMake configuration. + # Execute tests defined by the CMake configuration. # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail # run: ctest -C $BUILD_TYPE diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 43c3eb8..b7521a2 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -47,7 +47,7 @@ jobs: with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. + # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # queries: ./path/to/local/query, your-org/your-repo/queries@main diff --git a/README.md b/README.md index 3921f7a..8022333 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,6 @@ For ESP8266 and ESP32 [WifiManager](https://github.com/tzapu/WiFiManager) is use Don't forget to reset ESP8266 manually (disconnect power) after flashing. The reboot doen't work during configuration with ETS otherwise. -The SAMD21 version uses my version of the [FlashStorage](https://github.com/thelsing/FlashStorage) lib (Pull request pending). - Generated documentation can be found [here](https://knx.readthedocs.io/en/latest/). ## Stack configuration possibilities @@ -52,4 +50,4 @@ void main () } ``` -More configuration options can be found in the examples. \ No newline at end of file +More configuration options can be found in the examples. diff --git a/examples/knx-demo-coupler/platformio-ci.ini b/examples/knx-demo-coupler/platformio-ci.ini index 4a3e3d9..120997d 100644 --- a/examples/knx-demo-coupler/platformio-ci.ini +++ b/examples/knx-demo-coupler/platformio-ci.ini @@ -15,7 +15,6 @@ board = adafruit_feather_m0 framework = arduino lib_deps = SPI - https://github.com/thelsing/FlashStorage.git knx build_flags = diff --git a/examples/knx-demo-coupler/platformio.ini b/examples/knx-demo-coupler/platformio.ini index 18169f8..fbbc585 100644 --- a/examples/knx-demo-coupler/platformio.ini +++ b/examples/knx-demo-coupler/platformio.ini @@ -24,7 +24,6 @@ lib_extra_dirs = ../../../ lib_deps = SPI - https://github.com/thelsing/FlashStorage.git knx build_flags = diff --git a/examples/knx-demo/platformio-ci.ini b/examples/knx-demo/platformio-ci.ini index d2056ac..2f0b04c 100644 --- a/examples/knx-demo/platformio-ci.ini +++ b/examples/knx-demo/platformio-ci.ini @@ -15,7 +15,6 @@ board = adafruit_feather_m0 framework = arduino lib_deps = SPI - https://github.com/thelsing/FlashStorage.git knx build_flags = diff --git a/examples/knx-demo/platformio.ini b/examples/knx-demo/platformio.ini index a706ccb..f47cade 100644 --- a/examples/knx-demo/platformio.ini +++ b/examples/knx-demo/platformio.ini @@ -24,7 +24,6 @@ lib_extra_dirs = ../../../ lib_deps = SPI - https://github.com/thelsing/FlashStorage.git knx build_flags = @@ -100,3 +99,50 @@ lib_deps = build_flags = -DMASK_VERSION=0x07B0 -Wno-unknown-pragmas + +;--- STM32/GD32 --- +[env:h8i8o] +platform = ststm32 +board = genericSTM32F103CB +framework = arduino +; We consider that the this projects is opened within its project directory +; while working with VS Code. +lib_extra_dirs = ../../../ + +lib_deps = + knx + +build_flags = + -DENABLE_HWSERIAL1 + -DPIN_SERIAL1_TX=PA9 + -DPIN_SERIAL1_RX=PA10 + -DKNX_SERIAL=Serial1 + -DKNX_BUTTON=PA11 + -DKNX_LED=PA12 + -DMASK_VERSION=0x07B0 + -Wno-unknown-pragmas + +extra_scripts = ../scripts/stm32rdu.py + +[env:h8c09] +platform = ststm32 +board = genericSTM32F103CB +framework = arduino +; We consider that the this projects is opened within its project directory +; while working with VS Code. +lib_extra_dirs = ../../../ + +lib_deps = + knx + +build_flags = + -DENABLE_HWSERIAL1 + -DPIN_SERIAL1_TX=PA9 + -DPIN_SERIAL1_RX=PA10 + -DKNX_SERIAL=Serial1 + -DKNX_BUTTON=PB0 + -DKNX_LED=PB5 + -DMASK_VERSION=0x07B0 + -Wno-unknown-pragmas + +extra_scripts = ../scripts/stm32rdu.py diff --git a/examples/knx-h8i8o/knx-h8i8o.ino b/examples/knx-h8i8o/knx-h8i8o.ino new file mode 100644 index 0000000..1c3023c --- /dev/null +++ b/examples/knx-h8i8o/knx-h8i8o.ino @@ -0,0 +1,210 @@ +#include +#include + +#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; +} diff --git a/examples/knx-h8i8o/knx-h8i8o.knxprod b/examples/knx-h8i8o/knx-h8i8o.knxprod new file mode 100644 index 0000000..cf848d3 Binary files /dev/null and b/examples/knx-h8i8o/knx-h8i8o.knxprod differ diff --git a/examples/knx-h8i8o/knx-h8i8o.xml b/examples/knx-h8i8o/knx-h8i8o.xml new file mode 100644 index 0000000..07b969a --- /dev/null +++ b/examples/knx-h8i8o/knx-h8i8o.xml @@ -0,0 +1,2198 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/knx-h8i8o/platformio.ini b/examples/knx-h8i8o/platformio.ini new file mode 100644 index 0000000..f1340e1 --- /dev/null +++ b/examples/knx-h8i8o/platformio.ini @@ -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 diff --git a/examples/knx-usb/platformio-ci.ini b/examples/knx-usb/platformio-ci.ini index ce01930..60cc010 100644 --- a/examples/knx-usb/platformio-ci.ini +++ b/examples/knx-usb/platformio-ci.ini @@ -20,7 +20,6 @@ board_build.usb_product="KNX RF - USB Interface" lib_deps = SPI Adafruit TinyUSB Library@0.7.1 - https://github.com/thelsing/FlashStorage.git knx build_flags = diff --git a/examples/scripts/stm32rdu.py b/examples/scripts/stm32rdu.py new file mode 100755 index 0000000..d0c5400 --- /dev/null +++ b/examples/scripts/stm32rdu.py @@ -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) diff --git a/src/arduino_platform.cpp b/src/arduino_platform.cpp index 8c58dfb..62ab8a9 100644 --- a/src/arduino_platform.cpp +++ b/src/arduino_platform.cpp @@ -7,7 +7,7 @@ #endif #ifndef KNX_NO_PRINT -Stream* ArduinoPlatform::SerialDebug = &Serial; +Stream* ArduinoPlatform::SerialDebug = &KNX_DEBUG_SERIAL; #endif ArduinoPlatform::ArduinoPlatform() : _knxSerial(nullptr) @@ -22,13 +22,13 @@ void ArduinoPlatform::fatalError() { while (true) { -#ifdef LED_BUILTIN +#ifdef KNX_LED static const long LED_BLINK_PERIOD = 200; if ((millis() % LED_BLINK_PERIOD) > (LED_BLINK_PERIOD / 2)) - digitalWrite(LED_BUILTIN, HIGH); + digitalWrite(KNX_LED, HIGH); else - digitalWrite(LED_BUILTIN, LOW); + digitalWrite(KNX_LED, LOW); #endif } } diff --git a/src/arduino_platform.h b/src/arduino_platform.h index 3ef5047..29d846e 100644 --- a/src/arduino_platform.h +++ b/src/arduino_platform.h @@ -2,6 +2,10 @@ #include "Arduino.h" +#ifndef KNX_DEBUG_SERIAL +#define KNX_DEBUG_SERIAL Serial +#endif + class ArduinoPlatform : public Platform { public: diff --git a/src/cc1310_platform.h b/src/cc1310_platform.h index dac59a6..17086ab 100644 --- a/src/cc1310_platform.h +++ b/src/cc1310_platform.h @@ -17,11 +17,11 @@ class CC1310Platform : public Platform void init(); // basic stuff - virtual void restart() final; - virtual void fatalError() final; + void restart() final; + void fatalError() final; - virtual uint8_t* getEepromBuffer(uint16_t size) final; - virtual void commitToEeprom() final; + uint8_t* getEepromBuffer(uint16_t size) final; + void commitToEeprom() final; }; #endif //DeviceFamily_CC13X0 diff --git a/src/esp32_platform.cpp b/src/esp32_platform.cpp index 6f49934..f6c4812 100644 --- a/src/esp32_platform.cpp +++ b/src/esp32_platform.cpp @@ -6,9 +6,13 @@ #include "knx/bits.h" +#ifndef KNX_SERIAL +#define KNX_SERIAL Serial1 +#endif + Esp32Platform::Esp32Platform() #ifndef KNX_NO_DEFAULT_UART - : ArduinoPlatform(&Serial1) + : ArduinoPlatform(&KNX_SERIAL) #endif { } @@ -55,10 +59,10 @@ void Esp32Platform::setupMultiCast(uint32_t addr, uint16_t port) { IPAddress mcastaddr(htonl(addr)); - Serial.printf("setup multicast addr: %s port: %d ip: %s\n", mcastaddr.toString().c_str(), port, + KNX_DEBUG_SERIAL.printf("setup multicast addr: %s port: %d ip: %s\n", mcastaddr.toString().c_str(), port, WiFi.localIP().toString().c_str()); uint8_t result = _udp.beginMulticast(mcastaddr, port); - Serial.printf("result %d\n", result); + KNX_DEBUG_SERIAL.printf("result %d\n", result); } void Esp32Platform::closeMultiCast() @@ -83,7 +87,7 @@ int Esp32Platform::readBytesMultiCast(uint8_t * buffer, uint16_t maxLen) if (len > maxLen) { - Serial.printf("udp buffer to small. was %d, needed %d\n", maxLen, len); + KNX_DEBUG_SERIAL.printf("udp buffer to small. was %d, needed %d\n", maxLen, len); fatalError(); } @@ -106,8 +110,12 @@ bool Esp32Platform::sendBytesUniCast(uint32_t addr, uint16_t port, uint8_t* buff uint8_t * Esp32Platform::getEepromBuffer(uint16_t size) { - EEPROM.begin(size); - return EEPROM.getDataPtr(); + uint8_t * eepromptr = EEPROM.getDataPtr(); + if(eepromptr == nullptr) { + EEPROM.begin(size); + eepromptr = EEPROM.getDataPtr(); + } + return eepromptr; } void Esp32Platform::commitToEeprom() diff --git a/src/esp_platform.cpp b/src/esp_platform.cpp index 14a7c58..b74dedd 100644 --- a/src/esp_platform.cpp +++ b/src/esp_platform.cpp @@ -7,9 +7,13 @@ #include "knx/bits.h" +#ifndef KNX_SERIAL +#define KNX_SERIAL Serial +#endif + EspPlatform::EspPlatform() #ifndef KNX_NO_DEFAULT_UART - : ArduinoPlatform(&Serial) + : ArduinoPlatform(&KNX_SERIAL) #endif { } @@ -55,10 +59,10 @@ void EspPlatform::setupMultiCast(uint32_t addr, uint16_t port) _multicastPort = port; IPAddress mcastaddr(_multicastAddr); - Serial.printf("setup multicast addr: %s port: %d ip: %s\n", mcastaddr.toString().c_str(), port, + KNX_DEBUG_SERIAL.printf("setup multicast addr: %s port: %d ip: %s\n", mcastaddr.toString().c_str(), port, WiFi.localIP().toString().c_str()); uint8 result = _udp.beginMulticast(WiFi.localIP(), mcastaddr, port); - Serial.printf("result %d\n", result); + KNX_DEBUG_SERIAL.printf("result %d\n", result); } void EspPlatform::closeMultiCast() @@ -83,7 +87,7 @@ int EspPlatform::readBytesMultiCast(uint8_t * buffer, uint16_t maxLen) if (len > maxLen) { - Serial.printf("udp buffer to small. was %d, needed %d\n", maxLen, len); + KNX_DEBUG_SERIAL.printf("udp buffer to small. was %d, needed %d\n", maxLen, len); fatalError(); } @@ -106,8 +110,12 @@ bool EspPlatform::sendBytesUniCast(uint32_t addr, uint16_t port, uint8_t* buffer uint8_t * EspPlatform::getEepromBuffer(uint16_t size) { - EEPROM.begin(size); - return EEPROM.getDataPtr(); + uint8_t * eepromptr = EEPROM.getDataPtr(); + if(eepromptr == nullptr) { + EEPROM.begin(size); + eepromptr = EEPROM.getDataPtr(); + } + return eepromptr; } void EspPlatform::commitToEeprom() diff --git a/src/knx/address_table_object.cpp b/src/knx/address_table_object.cpp index 5c90b40..e789840 100644 --- a/src/knx/address_table_object.cpp +++ b/src/knx/address_table_object.cpp @@ -19,7 +19,8 @@ AddressTableObject::AddressTableObject(Memory& memory) uint16_t AddressTableObject::entryCount() { - if (loadState() != LS_LOADED) + // after programming without GA the module hangs + if (loadState() != LS_LOADED || _groupAddresses[0] == 0xFFFF) return 0; return ntohs(_groupAddresses[0]); @@ -104,6 +105,7 @@ bool AddressTableObject::contains(uint16_t addr) void AddressTableObject::beforeStateChange(LoadState& newState) { + TableObject::beforeStateChange(newState); if (newState != LS_LOADED) return; diff --git a/src/knx/address_table_object.h b/src/knx/address_table_object.h index a8990c2..13b1ebb 100644 --- a/src/knx/address_table_object.h +++ b/src/knx/address_table_object.h @@ -50,7 +50,7 @@ class AddressTableObject : public TableObject bool contains(uint16_t groupAddress); protected: - virtual void beforeStateChange(LoadState& newState) override; + void beforeStateChange(LoadState& newState) override; private: uint16_t* _groupAddresses = 0; diff --git a/src/knx/application_layer.cpp b/src/knx/application_layer.cpp index 581f8bd..5e5f6b5 100644 --- a/src/knx/application_layer.cpp +++ b/src/knx/application_layer.cpp @@ -347,7 +347,7 @@ void ApplicationLayer::dataConnectedConfirm(uint16_t tsap) void ApplicationLayer::dataConnectedConfirm(uint16_t tsap, const SecurityControl& secCtrl) { - + //FIXME: implement dataConnectedConfirm DataSecurity } #pragma endregion void ApplicationLayer::groupValueReadRequest(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl& secCtrl) diff --git a/src/knx/application_layer.h b/src/knx/application_layer.h index c2d4578..327aed1 100644 --- a/src/knx/application_layer.h +++ b/src/knx/application_layer.h @@ -157,7 +157,6 @@ class ApplicationLayer #pragma endregion protected: - #pragma region hooks void dataGroupIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, const SecurityControl &secCtrl); void dataGroupConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, diff --git a/src/knx/association_table_object.cpp b/src/knx/association_table_object.cpp index b1d8c56..13bcfc9 100644 --- a/src/knx/association_table_object.cpp +++ b/src/knx/association_table_object.cpp @@ -84,6 +84,7 @@ int32_t AssociationTableObject::translateAsap(uint16_t asap) void AssociationTableObject::beforeStateChange(LoadState& newState) { + TableObject::beforeStateChange(newState); if (newState != LS_LOADED) return; diff --git a/src/knx/bau.cpp b/src/knx/bau.cpp index a74e7bf..24f8138 100644 --- a/src/knx/bau.cpp +++ b/src/knx/bau.cpp @@ -338,3 +338,12 @@ void BusAccessUnit::propertyValueWrite(ObjectType objectType, uint8_t objectInst uint8_t* data, uint32_t length) { } + +void BusAccessUnit::beforeRestartCallback(BeforeRestartCallback func) +{ +} + +BeforeRestartCallback BusAccessUnit::beforeRestartCallback() +{ + return 0; +} diff --git a/src/knx/bau.h b/src/knx/bau.h index 78b3580..1382446 100644 --- a/src/knx/bau.h +++ b/src/knx/bau.h @@ -3,6 +3,8 @@ #include "knx_types.h" #include "interface_object.h" +typedef void (*BeforeRestartCallback)(void); + class BusAccessUnit { public: @@ -161,4 +163,6 @@ class BusAccessUnit virtual void propertyValueWrite(ObjectType objectType, uint8_t objectInstance, uint8_t propertyId, uint8_t& numberOfElements, uint16_t startIndex, uint8_t* data, uint32_t length); + virtual void beforeRestartCallback(BeforeRestartCallback func); + virtual BeforeRestartCallback beforeRestartCallback(); }; diff --git a/src/knx/bau07B0.h b/src/knx/bau07B0.h index 7828e41..3b29d72 100644 --- a/src/knx/bau07B0.h +++ b/src/knx/bau07B0.h @@ -12,16 +12,16 @@ class Bau07B0 : public BauSystemBDevice, public ITpUartCallBacks { public: Bau07B0(Platform& platform); - virtual void loop() override; - virtual bool enabled() override; - virtual void enabled(bool value) override; + void loop() override; + bool enabled() override; + void enabled(bool value) override; protected: InterfaceObject* getInterfaceObject(uint8_t idx); InterfaceObject* getInterfaceObject(ObjectType objectType, uint8_t objectInstance); // For TP1 only - virtual bool isAckRequired(uint16_t address, bool isGrpAddr) override; + bool isAckRequired(uint16_t address, bool isGrpAddr) override; private: TpUartDataLinkLayer _dlLayer; diff --git a/src/knx/bau091A.h b/src/knx/bau091A.h index 262055b..7c9b8e5 100644 --- a/src/knx/bau091A.h +++ b/src/knx/bau091A.h @@ -14,18 +14,18 @@ class Bau091A : public BauSystemBCoupler, public ITpUartCallBacks { public: Bau091A(Platform& platform); - virtual void loop() override; - virtual bool enabled() override; - virtual void enabled(bool value) override; + void loop() override; + bool enabled() override; + void enabled(bool value) override; protected: InterfaceObject* getInterfaceObject(uint8_t idx); InterfaceObject* getInterfaceObject(ObjectType objectType, uint8_t objectInstance); // For TP1 only - virtual bool isAckRequired(uint16_t address, bool isGrpAddr) override; + bool isAckRequired(uint16_t address, bool isGrpAddr) override; - virtual void doMasterReset(EraseCode eraseCode, uint8_t channel) override; + void doMasterReset(EraseCode eraseCode, uint8_t channel) override; private: RouterObject _routerObj; IpParameterObject _ipParameters; diff --git a/src/knx/bau27B0.h b/src/knx/bau27B0.h index 4d8b7fc..222a848 100644 --- a/src/knx/bau27B0.h +++ b/src/knx/bau27B0.h @@ -18,15 +18,15 @@ class Bau27B0 : public BauSystemBDevice { public: Bau27B0(Platform& platform); - virtual void loop() override; - virtual bool enabled() override; - virtual void enabled(bool value) override; + void loop() override; + bool enabled() override; + void enabled(bool value) override; protected: InterfaceObject* getInterfaceObject(uint8_t idx); InterfaceObject* getInterfaceObject(ObjectType objectType, uint8_t objectInstance); - virtual void doMasterReset(EraseCode eraseCode, uint8_t channel) override; + void doMasterReset(EraseCode eraseCode, uint8_t channel) override; private: RfDataLinkLayer _dlLayer; RfMediumObject _rfMediumObj; diff --git a/src/knx/bau2920.h b/src/knx/bau2920.h index 1b4a32e..43a5bc5 100644 --- a/src/knx/bau2920.h +++ b/src/knx/bau2920.h @@ -18,15 +18,15 @@ class Bau2920 : public BauSystemBCoupler { public: Bau2920(Platform& platform); - virtual void loop() override; - virtual bool enabled() override; - virtual void enabled(bool value) override; + void loop() override; + bool enabled() override; + void enabled(bool value) override; protected: InterfaceObject* getInterfaceObject(uint8_t idx); InterfaceObject* getInterfaceObject(ObjectType objectType, uint8_t objectInstance); - virtual void doMasterReset(EraseCode eraseCode, uint8_t channel) override; + void doMasterReset(EraseCode eraseCode, uint8_t channel) override; private: RouterObject _rtObjPrimary; RouterObject _rtObjSecondary; diff --git a/src/knx/bau57B0.h b/src/knx/bau57B0.h index 3c017c1..a381ebb 100644 --- a/src/knx/bau57B0.h +++ b/src/knx/bau57B0.h @@ -12,15 +12,15 @@ class Bau57B0 : public BauSystemBDevice { public: Bau57B0(Platform& platform); - virtual void loop() override; - virtual bool enabled() override; - virtual void enabled(bool value) override; + void loop() override; + bool enabled() override; + void enabled(bool value) override; protected: InterfaceObject* getInterfaceObject(uint8_t idx); InterfaceObject* getInterfaceObject(ObjectType objectType, uint8_t objectInstance); - virtual void doMasterReset(EraseCode eraseCode, uint8_t channel) override; + void doMasterReset(EraseCode eraseCode, uint8_t channel) override; private: IpParameterObject _ipParameters; IpDataLinkLayer _dlLayer; diff --git a/src/knx/bau_systemB.cpp b/src/knx/bau_systemB.cpp index 7eee281..572dd4a 100644 --- a/src/knx/bau_systemB.cpp +++ b/src/knx/bau_systemB.cpp @@ -31,6 +31,11 @@ void BauSystemB::writeMemory() _memory.writeMemory(); } +Platform& BauSystemB::platform() +{ + return _platform; +} + ApplicationProgramObject& BauSystemB::parameters() { return _appProgram; @@ -112,9 +117,14 @@ void BauSystemB::memoryWriteIndication(Priority priority, HopCountType hopType, uint16_t memoryAddress, uint8_t * data) { _memory.writeMemory(memoryAddress, number, data); - if (_deviceObj.verifyMode()) - memoryReadIndication(priority, hopType, asap, secCtrl, number, memoryAddress); + memoryReadIndication(priority, hopType, asap, secCtrl, number, memoryAddress, data); +} + +void BauSystemB::memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, + uint16_t memoryAddress, uint8_t * data) +{ + applicationLayer().memoryReadResponse(AckRequested, priority, hopType, asap, secCtrl, number, memoryAddress, data); } void BauSystemB::memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, @@ -147,6 +157,8 @@ void BauSystemB::restartRequestIndication(Priority priority, HopCountType hopTyp if (restartType == RestartType::BasicRestart) { println("Basic restart requested"); + if (_beforeRestart != 0) + _beforeRestart(); } else if (restartType == RestartType::MasterReset) { @@ -606,3 +618,23 @@ Memory& BauSystemB::memory() { return _memory; } + +void BauSystemB::versionCheckCallback(VersionCheckCallback func) +{ + _memory.versionCheckCallback(func); +} + +VersionCheckCallback BauSystemB::versionCheckCallback() +{ + return _memory.versionCheckCallback(); +} + +void BauSystemB::beforeRestartCallback(BeforeRestartCallback func) +{ + _beforeRestart = func; +} + +BeforeRestartCallback BauSystemB::beforeRestartCallback() +{ + return _beforeRestart; +} diff --git a/src/knx/bau_systemB.h b/src/knx/bau_systemB.h index 290f839..dfbced6 100644 --- a/src/knx/bau_systemB.h +++ b/src/knx/bau_systemB.h @@ -21,6 +21,7 @@ class BauSystemB : protected BusAccessUnit virtual bool enabled() = 0; virtual void enabled(bool value) = 0; + Platform& platform(); ApplicationProgramObject& parameters(); DeviceObject& deviceObject(); @@ -38,6 +39,10 @@ class BauSystemB : protected BusAccessUnit void propertyValueWrite(ObjectType objectType, uint8_t objectInstance, uint8_t propertyId, uint8_t& numberOfElements, uint16_t startIndex, uint8_t* data, uint32_t length) override; + void versionCheckCallback(VersionCheckCallback func); + VersionCheckCallback versionCheckCallback(); + void beforeRestartCallback(BeforeRestartCallback func); + BeforeRestartCallback beforeRestartCallback(); protected: virtual ApplicationLayer& applicationLayer() = 0; @@ -48,6 +53,8 @@ class BauSystemB : protected BusAccessUnit uint16_t memoryAddress, uint8_t* data) override; void memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress) override; + void memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, + uint16_t memoryAddress, uint8_t * data); void memoryExtWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress, uint8_t* data) override; void memoryExtReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, @@ -105,4 +112,5 @@ class BauSystemB : protected BusAccessUnit RestartState _restartState = Idle; SecurityControl _restartSecurity; uint32_t _restartDelay = 0; + BeforeRestartCallback _beforeRestart = 0; }; diff --git a/src/knx/bau_systemB_coupler.cpp b/src/knx/bau_systemB_coupler.cpp index c649378..d9e92ce 100644 --- a/src/knx/bau_systemB_coupler.cpp +++ b/src/knx/bau_systemB_coupler.cpp @@ -21,7 +21,6 @@ BauSystemBCoupler::BauSystemBCoupler(Platform& platform) : #ifdef USE_DATASECURE _memory.addSaveRestore(&_secIfObj); #endif - } ApplicationLayer& BauSystemBCoupler::applicationLayer() diff --git a/src/knx/bau_systemB_coupler.h b/src/knx/bau_systemB_coupler.h index f9dbacc..2cfc5be 100644 --- a/src/knx/bau_systemB_coupler.h +++ b/src/knx/bau_systemB_coupler.h @@ -18,13 +18,13 @@ class BauSystemBCoupler : public BauSystemB { public: BauSystemBCoupler(Platform& platform); - virtual void loop() override; - virtual bool configured() override; + void loop() override; + bool configured() override; protected: - virtual ApplicationLayer& applicationLayer() override; + ApplicationLayer& applicationLayer() override; - virtual void doMasterReset(EraseCode eraseCode, uint8_t channel) override; + void doMasterReset(EraseCode eraseCode, uint8_t channel) override; Platform& _platform; diff --git a/src/knx/bau_systemB_device.cpp b/src/knx/bau_systemB_device.cpp index 37289af..3ee4425 100644 --- a/src/knx/bau_systemB_device.cpp +++ b/src/knx/bau_systemB_device.cpp @@ -23,9 +23,9 @@ BauSystemBDevice::BauSystemBDevice(Platform& platform) : _transLayer.groupAddressTable(_addrTable); _memory.addSaveRestore(&_deviceObj); + _memory.addSaveRestore(&_groupObjTable); // changed order for better memory management _memory.addSaveRestore(&_addrTable); _memory.addSaveRestore(&_assocTable); - _memory.addSaveRestore(&_groupObjTable); #ifdef USE_DATASECURE _memory.addSaveRestore(&_secIfObj); #endif @@ -69,8 +69,21 @@ void BauSystemBDevice::sendNextGroupTelegram() if (flag != ReadRequest && flag != WriteRequest) continue; + if (flag == WriteRequest) + { +#ifdef SMALL_GROUPOBJECT + GroupObject::processClassCallback(go); +#else + GroupObjectUpdatedHandler handler = go.callback(); + if (handler) + handler(go); +#endif + } if (!go.communicationEnable()) + { + go.commFlag(Ok); continue; + } SecurityControl goSecurity; goSecurity.toolAccess = false; // Secured group communication never uses the toolkey. ETS knows all keys, also the group keys. @@ -113,14 +126,21 @@ void BauSystemBDevice::updateGroupObject(GroupObject & go, uint8_t * data, uint8 memcpy(goData, data, length); - go.commFlag(Updated); + if (go.commFlag() != WriteRequest) + { + go.commFlag(Updated); #ifdef SMALL_GROUPOBJECT - GroupObject::processClassCallback(go); + GroupObject::processClassCallback(go); #else - GroupObjectUpdatedHandler handler = go.callback(); - if (handler) - handler(go); + GroupObjectUpdatedHandler handler = go.callback(); + if (handler) + handler(go); #endif + } + else + { + go.commFlag(Updated); + } } bool BauSystemBDevice::configured() diff --git a/src/knx/bau_systemB_device.h b/src/knx/bau_systemB_device.h index ab645fe..1dadf87 100644 --- a/src/knx/bau_systemB_device.h +++ b/src/knx/bau_systemB_device.h @@ -20,12 +20,12 @@ class BauSystemBDevice : public BauSystemB { public: BauSystemBDevice(Platform& platform); - virtual void loop() override; - virtual bool configured() override; + void loop() override; + bool configured() override; GroupObjectTableObject& groupObjectTable(); protected: - virtual ApplicationLayer& applicationLayer() override; + ApplicationLayer& applicationLayer() override; void groupValueWriteLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint8_t* data, uint8_t dataLength, bool status) override; @@ -39,7 +39,7 @@ class BauSystemBDevice : public BauSystemB void sendNextGroupTelegram(); void updateGroupObject(GroupObject& go, uint8_t* data, uint8_t length); - virtual void doMasterReset(EraseCode eraseCode, uint8_t channel) override; + void doMasterReset(EraseCode eraseCode, uint8_t channel) override; AddressTableObject _addrTable; AssociationTableObject _assocTable; diff --git a/src/knx/bits.cpp b/src/knx/bits.cpp index 9b04145..d69f17d 100644 --- a/src/knx/bits.cpp +++ b/src/knx/bits.cpp @@ -124,21 +124,19 @@ uint64_t sixBytesToUInt64(uint8_t* data) uint16_t crc16Ccitt(uint8_t* input, uint16_t length) { - uint32_t polynom = 0x1021; - uint8_t padded[length+2]; + uint32_t polynom = 0x1021; - memcpy(padded, input, length); - memset(padded+length, 0x00, 2); - - uint32_t result = 0xffff; - for (uint32_t i = 0; i < 8 * (uint32_t)sizeof(padded); i++) { - result <<= 1; - uint32_t nextBit = (padded[i / 8] >> (7 - (i % 8))) & 0x1; - result |= nextBit; - if ((result & 0x10000) != 0) - result ^= polynom; - } - return result & 0xffff; + uint32_t result = 0xffff; + for (uint32_t i = 0; i < 8 * ((uint32_t)length + 2); i++) + { + result <<= 1; + uint32_t nextBit; + nextBit = ((i / 8) < length) ? ((input[i / 8] >> (7 - (i % 8))) & 0x1) : 0; + result |= nextBit; + if ((result & 0x10000) != 0) + result ^= polynom; + } + return result & 0xffff; } uint16_t crc16Dnp(uint8_t* input, uint16_t length) diff --git a/src/knx/callback_property.h b/src/knx/callback_property.h index b90e397..542b659 100644 --- a/src/knx/callback_property.h +++ b/src/knx/callback_property.h @@ -18,14 +18,14 @@ template class CallbackProperty : public Property : Property(id, writeEnable, type, maxElements, access), _interfaceObject(io), _readCallback(readCallback) {} - virtual uint8_t read(uint16_t start, uint8_t count, uint8_t* data) const override + uint8_t read(uint16_t start, uint8_t count, uint8_t* data) const override { if (count == 0 || _readCallback == nullptr || start > _maxElements || start + count > _maxElements + 1) return 0; return _readCallback(_interfaceObject, start, count, data); } - virtual uint8_t write(uint16_t start, uint8_t count, const uint8_t* data) override + uint8_t write(uint16_t start, uint8_t count, const uint8_t* data) override { if (count == 0 || start > _maxElements || start + count > _maxElements + 1 || _writeCallback == nullptr) return 0; diff --git a/src/knx/data_property.h b/src/knx/data_property.h index 12c202f..03f344f 100644 --- a/src/knx/data_property.h +++ b/src/knx/data_property.h @@ -10,12 +10,12 @@ class DataProperty : public Property DataProperty(PropertyID id, bool writeEnable, PropertyDataType type, uint16_t maxElements, uint8_t access, uint16_t value); DataProperty(PropertyID id, bool writeEnable, PropertyDataType type, uint16_t maxElements, uint8_t access, uint32_t value); DataProperty(PropertyID id, bool writeEnable, PropertyDataType type, uint16_t maxElements, uint8_t access, const uint8_t* value); - virtual ~DataProperty() override; - virtual uint8_t read(uint16_t start, uint8_t count, uint8_t* data) const override; - virtual uint8_t write(uint16_t start, uint8_t count, const uint8_t* data) override; - virtual uint8_t* save(uint8_t* buffer) override; - virtual const uint8_t* restore(const uint8_t* buffer) override; - virtual uint16_t saveSize() override; + ~DataProperty() override; + uint8_t read(uint16_t start, uint8_t count, uint8_t* data) const override; + uint8_t write(uint16_t start, uint8_t count, const uint8_t* data) override; + uint8_t* save(uint8_t* buffer) override; + const uint8_t* restore(const uint8_t* buffer) override; + uint16_t saveSize() override; const uint8_t* data(); const uint8_t* data(uint16_t elementIndex); diff --git a/src/knx/device_object.cpp b/src/knx/device_object.cpp index 91e88be..4c83211 100644 --- a/src/knx/device_object.cpp +++ b/src/knx/device_object.cpp @@ -91,7 +91,6 @@ DeviceObject::DeviceObject() #ifdef USE_RF new DataProperty(PID_RF_DOMAIN_ADDRESS_CEMI_SERVER, true, PDT_GENERIC_06, 1, ReadLv3 | WriteLv3), #endif - }; initializeProperties(sizeof(properties), properties); } diff --git a/src/knx/device_object.h b/src/knx/device_object.h index a7d80d4..10f171d 100644 --- a/src/knx/device_object.h +++ b/src/knx/device_object.h @@ -7,6 +7,10 @@ class DeviceObject: public InterfaceObject { public: + // increase this version anytime DeviceObject-API changes + // the following value represents the serialized representation of DeviceObject. + const uint16_t apiVersion = 1; + DeviceObject(); uint8_t* save(uint8_t* buffer) override; const uint8_t* restore(const uint8_t* buffer) override; diff --git a/src/knx/dptconvert.cpp b/src/knx/dptconvert.cpp index 6fc644f..f687533 100644 --- a/src/knx/dptconvert.cpp +++ b/src/knx/dptconvert.cpp @@ -135,7 +135,6 @@ int KNX_Decode_Value(uint8_t* payload, size_t payload_length, const Dpt& datatyp int KNX_Encode_Value(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype) { - if (datatype.mainGroup == 1 && datatype.subGroup >= 1 && datatype.subGroup <= 23 && datatype.subGroup != 20 && !datatype.index) return valueToBusValueBinary(value, payload, payload_length, datatype); // DPT 2.* - Binary Control @@ -1553,7 +1552,6 @@ int valueToBusValueRGBW(const KNXValue& value, uint8_t* payload, size_t payload_ case 1: // Mask bits unsigned8ToPayload(payload, payload_length, 5, (uint8_t)value, 0x0f); break; - } return true; } @@ -1757,7 +1755,7 @@ void float16ToPayload(uint8_t* payload, size_t payload_length, int index, double if (wasNegative) mantissa *= -1; - println(mantissa); + // println(mantissa); signed16ToPayload(payload, payload_length, index, mantissa, mask); unsigned8ToPayload(payload, payload_length, index, exponent << 3, 0x78 & (mask >> 8)); diff --git a/src/knx/function_property.h b/src/knx/function_property.h index 78e6464..361d52b 100644 --- a/src/knx/function_property.h +++ b/src/knx/function_property.h @@ -14,17 +14,17 @@ template class FunctionProperty : public Property /* max_elements is set to 1, read and write level any value so we use Lv0, see 3.3.7 Application Layer p.68 */ {} - virtual uint8_t read(uint16_t start, uint8_t count, uint8_t* data) const override + uint8_t read(uint16_t start, uint8_t count, uint8_t* data) const override { return 0; } - virtual uint8_t write(uint16_t start, uint8_t count, const uint8_t* data) override + uint8_t write(uint16_t start, uint8_t count, const uint8_t* data) override { return 0; } - virtual void command(uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) override + void command(uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) override { if (length == 0 || _commandCallback == nullptr ) { @@ -34,7 +34,7 @@ template class FunctionProperty : public Property _commandCallback(_interfaceObject, data, length, resultData, resultLength); } - virtual void state(uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) override + void state(uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) override { if (length == 0 || _stateCallback == nullptr ) { diff --git a/src/knx/group_object.cpp b/src/knx/group_object.cpp index db92058..6898ba5 100644 --- a/src/knx/group_object.cpp +++ b/src/knx/group_object.cpp @@ -12,7 +12,7 @@ GroupObjectTableObject* GroupObject::_table = 0; GroupObject::GroupObject() { _data = 0; - _commFlag = Ok; + _commFlag = Uninitialized; _dataLength = 0; #ifndef SMALL_GROUPOBJECT _updateHandler = 0; @@ -74,6 +74,10 @@ bool GroupObject::readEnable() if (!_table) return false; + // we forbid reading of new (uninitialized) go + if (_commFlag == Uninitialized) + return false; + return bitRead(ntohs(_table->_tableData[_asap]), 11) > 0; } @@ -270,5 +274,8 @@ void GroupObject::valueNoSend(const KNXValue& value) void GroupObject::valueNoSend(const KNXValue& value, const Dpt& type) { + if (_commFlag == Uninitialized) + _commFlag = Ok; + KNX_Encode_Value(value, _data, _dataLength, type); } diff --git a/src/knx/group_object.h b/src/knx/group_object.h index 91c8b56..88ce274 100644 --- a/src/knx/group_object.h +++ b/src/knx/group_object.h @@ -14,7 +14,8 @@ enum ComFlag WriteRequest = 2, //!< Write was requested but was not processed Transmitting = 3, //!< Group Object is processed a the moment (read or write) Ok = 4, //!< read or write request were send successfully - Error = 5 //!< there was an error on processing a request + Error = 5, //!< there was an error on processing a request + Uninitialized = 6 //!< uninitialized Group Object, its value is not valid }; class GroupObject; @@ -235,7 +236,7 @@ class GroupObject size_t asapValueSize(uint8_t code); size_t goSize(); uint16_t _asap = 0; - ComFlag _commFlag = Ok; + ComFlag _commFlag = Uninitialized; uint8_t* _data = 0; uint8_t _dataLength = 0; #ifndef SMALL_GROUPOBJECT diff --git a/src/knx/group_object_table_object.cpp b/src/knx/group_object_table_object.cpp index b610404..bdcf8dd 100644 --- a/src/knx/group_object_table_object.cpp +++ b/src/knx/group_object_table_object.cpp @@ -77,6 +77,7 @@ void GroupObjectTableObject::groupObjects(GroupObject * objs, uint16_t size) void GroupObjectTableObject::beforeStateChange(LoadState& newState) { + TableObject::beforeStateChange(newState); if (newState != LS_LOADED) return; diff --git a/src/knx/interface_object.h b/src/knx/interface_object.h index b287735..47b1124 100644 --- a/src/knx/interface_object.h +++ b/src/knx/interface_object.h @@ -183,12 +183,11 @@ class InterfaceObject : public SaveRestore */ const Property* property(PropertyID id) const; - virtual uint8_t* save(uint8_t* buffer) override; - virtual const uint8_t* restore(const uint8_t* buffer) override; - virtual uint16_t saveSize() override; + uint8_t* save(uint8_t* buffer) override; + const uint8_t* restore(const uint8_t* buffer) override; + uint16_t saveSize() override; protected: - /** * Intializes the Property-array the the supplied values. */ diff --git a/src/knx/ip_data_link_layer.h b/src/knx/ip_data_link_layer.h index 997a696..f8acae5 100644 --- a/src/knx/ip_data_link_layer.h +++ b/src/knx/ip_data_link_layer.h @@ -18,7 +18,7 @@ class IpDataLinkLayer : public DataLinkLayer void loop(); void enabled(bool value); bool enabled() const; - virtual DptMedium mediumType() const override; + DptMedium mediumType() const override; private: bool _enabled = false; diff --git a/src/knx/knx_ip_frame.h b/src/knx/knx_ip_frame.h index f486774..f8a421f 100644 --- a/src/knx/knx_ip_frame.h +++ b/src/knx/knx_ip_frame.h @@ -29,9 +29,6 @@ enum KnxIpServiceType TunnelingAck = 0x421, RoutingIndication = 0x530, RoutingLostMessage = 0x531, - - - }; class KnxIpFrame diff --git a/src/knx/knx_value.h b/src/knx/knx_value.h index e0e5792..b59c6cf 100644 --- a/src/knx/knx_value.h +++ b/src/knx/knx_value.h @@ -49,7 +49,6 @@ class KNXValue KNXValue& operator=(const float value); private: - bool boolValue() const; uint8_t ucharValue() const; uint16_t ushortValue() const; diff --git a/src/knx/memory.cpp b/src/knx/memory.cpp index a5945cb..5d33148 100644 --- a/src/knx/memory.cpp +++ b/src/knx/memory.cpp @@ -1,73 +1,115 @@ #include "memory.h" + #include + #include "bits.h" Memory::Memory(Platform& platform, DeviceObject& deviceObject) : _platform(platform), _deviceObject(deviceObject) -{ -} +{} + +Memory::~Memory() +{} void Memory::readMemory() { println("readMemory"); - if (_data != nullptr) + + uint8_t* flashStart = _platform.getNonVolatileMemoryStart(); + size_t flashSize = _platform.getNonVolatileMemorySize(); + if (flashStart == nullptr) + { + println("no user flash available;"); return; + } - uint16_t flashSize = KNX_FLASH_SIZE; - _data = _platform.getEepromBuffer(flashSize); - - printHex("RESTORED ", _data, _metadataSize); + printHex("RESTORED ", flashStart, _metadataSize); uint16_t metadataBlockSize = alignToPageSize(_metadataSize); - _freeList = new MemoryBlock(_data + metadataBlockSize, flashSize - metadataBlockSize); + _freeList = new MemoryBlock(flashStart + metadataBlockSize, flashSize - metadataBlockSize); + + uint16_t apiVersion = 0; + const uint8_t* buffer = popWord(apiVersion, flashStart); uint16_t manufacturerId = 0; - const uint8_t* buffer = popWord(manufacturerId, _data); + buffer = popWord(manufacturerId, buffer); uint8_t hardwareType[LEN_HARDWARE_TYPE] = {0}; buffer = popByteArray(hardwareType, LEN_HARDWARE_TYPE, buffer); uint16_t version = 0; buffer = popWord(version, buffer); - - - - if (_deviceObject.manufacturerId() != manufacturerId - || _deviceObject.version() != version - || memcmp(_deviceObject.hardwareType(), hardwareType, LEN_HARDWARE_TYPE) != 0) + + VersionCheckResult versionCheck = FlashAllInvalid; + + // first check correct format of deviceObject-API + if (_deviceObject.apiVersion == apiVersion) { - println("saved memory doesn't match manufacturerId, version or hardwaretype"); - print("manufacturerId: "); - print(manufacturerId, HEX); - print(" "); - println(_deviceObject.manufacturerId(), HEX); - print("version: "); - print(version, HEX); - print(" "); - println(_deviceObject.version(), HEX); - print("hardwareType: "); - printHex("", hardwareType, LEN_HARDWARE_TYPE); - print(" "); - printHex("", _deviceObject.hardwareType(), LEN_HARDWARE_TYPE); + if (_versionCheckCallback != 0) { + versionCheck = _versionCheckCallback(manufacturerId, hardwareType, version); + // callback should provide infomation about version check failure reasons + } + else if (_deviceObject.manufacturerId() == manufacturerId && + memcmp(_deviceObject.hardwareType(), hardwareType, LEN_HARDWARE_TYPE) == 0) + { + if (_deviceObject.version() == version) { + versionCheck = FlashValid; + } + else + { + versionCheck = FlashTablesInvalid; + } + } + else + { + println("manufacturerId or hardwareType are different"); + print("expexted manufacturerId: "); + print(_deviceObject.manufacturerId(), HEX); + print(", stored manufacturerId: "); + println(manufacturerId, HEX); + print("expexted hardwareType: "); + printHex("", _deviceObject.hardwareType(), LEN_HARDWARE_TYPE); + print(", stored hardwareType: "); + printHex("", hardwareType, LEN_HARDWARE_TYPE); + println(""); + } + } + else + { + println("DataObject api changed, any data stored in flash is invalid."); + print("expexted DataObject api version: "); + print(_deviceObject.apiVersion, HEX); + print(", stored api version: "); + println(apiVersion, HEX); + } + + if (versionCheck == FlashAllInvalid) + { + println("ETS has to reprogram PA and application!"); return; } - println("manufacturerId, version and hardwareType matches"); + println("restoring data from flash..."); print("saverestores "); println(_saveCount); for (int i = 0; i < _saveCount; i++) { - println(_data - buffer); + println(flashStart - buffer); println("."); buffer = _saveRestores[i]->restore(buffer); } println("restored saveRestores"); + if (versionCheck == FlashTablesInvalid) + { + println("TableObjects are referring to an older firmware version and are not loaded"); + return; + } print("tableObjs "); println(_tableObjCount); for (int i = 0; i < _tableObjCount; i++) { - println(_data - buffer); + println(flashStart - buffer); println("."); buffer = _tableObjects[i]->restore(buffer); uint16_t memorySize = 0; @@ -84,48 +126,62 @@ void Memory::readMemory() void Memory::writeMemory() { - uint8_t* buffer = _data; - buffer = pushWord(_deviceObject.manufacturerId(), buffer); - buffer = pushByteArray(_deviceObject.hardwareType(), LEN_HARDWARE_TYPE, buffer); - buffer = pushWord(_deviceObject.version(), buffer); + // first get the necessary size of the writeBuffer + size_t writeBufferSize = _metadataSize; + for (int i = 0; i < _saveCount; i++) + writeBufferSize = MAX(writeBufferSize, _saveRestores[i]->saveSize()); + + for (int i = 0; i < _tableObjCount; i++) + writeBufferSize = MAX(writeBufferSize, _tableObjects[i]->saveSize() + 2 /*for memory pos*/); + + uint8_t buffer[writeBufferSize]; + uint32_t flashPos = 0; + uint8_t* bufferPos = buffer; + + bufferPos = pushWord(_deviceObject.apiVersion, bufferPos); + bufferPos = pushWord(_deviceObject.manufacturerId(), bufferPos); + bufferPos = pushByteArray(_deviceObject.hardwareType(), LEN_HARDWARE_TYPE, bufferPos); + bufferPos = pushWord(_deviceObject.version(), bufferPos); + + flashPos = _platform.writeNonVolatileMemory(flashPos, buffer, bufferPos - buffer); print("save saveRestores "); println(_saveCount); for (int i = 0; i < _saveCount; i++) { - println(_data - buffer); - println("."); - //println((long)_saveRestores[i], HEX); - buffer = _saveRestores[i]->save(buffer); + bufferPos = _saveRestores[i]->save(buffer); + flashPos = _platform.writeNonVolatileMemory(flashPos, buffer, bufferPos - buffer); } print("save tableobjs "); println(_tableObjCount); for (int i = 0; i < _tableObjCount; i++) { - println(_data - buffer); - println("."); - //println((long)_tableObjects[i], HEX); - buffer = _tableObjects[i]->save(buffer); + bufferPos = _tableObjects[i]->save(buffer); //save to size of the memoryblock for tableobject too, so that we can rebuild the usedList and freeList if (_tableObjects[i]->_data != nullptr) { - MemoryBlock* block = findBlockInList(_usedList, _tableObjects[i]->_data); if (block == nullptr) { - println("_data of TableObject not in errorlist"); + println("_data of TableObject not in _usedList"); _platform.fatalError(); } - buffer = pushWord(block->size, buffer); + bufferPos = pushWord(block->size, bufferPos); } else - buffer = pushWord(0, buffer); + bufferPos = pushWord(0, bufferPos); + + flashPos = _platform.writeNonVolatileMemory(flashPos, buffer, bufferPos - buffer); } - _platform.commitToEeprom(); - printHex("SAVED ", _data, _metadataSize); + _platform.commitNonVolatileMemory(); +} + +void Memory::saveMemory() +{ + _platform.commitNonVolatileMemory(); } void Memory::addSaveRestore(SaveRestore* obj) @@ -151,7 +207,7 @@ void Memory::addSaveRestore(TableObject* obj) uint8_t* Memory::allocMemory(size_t size) { - // always allocate aligned to 32 bit + // always allocate aligned to pagesize size = alignToPageSize(size); MemoryBlock* freeBlock = _freeList; @@ -220,19 +276,19 @@ void Memory::freeMemory(uint8_t* ptr) void Memory::writeMemory(uint32_t relativeAddress, size_t size, uint8_t* data) { - memcpy(toAbsolute(relativeAddress), data, size); + _platform.writeNonVolatileMemory(relativeAddress, data, size); } uint8_t* Memory::toAbsolute(uint32_t relativeAddress) { - return _data + (ptrdiff_t)relativeAddress; + return _platform.getNonVolatileMemoryStart() + (ptrdiff_t)relativeAddress; } uint32_t Memory::toRelative(uint8_t* absoluteAddress) { - return absoluteAddress - _data; + return absoluteAddress - _platform.getNonVolatileMemoryStart(); } MemoryBlock* Memory::removeFromList(MemoryBlock* head, MemoryBlock* item) @@ -354,8 +410,9 @@ void Memory::addToFreeList(MemoryBlock* block) uint16_t Memory::alignToPageSize(size_t size) { - // to 32 bit for now - return (size + 3) & ~0x3; + size_t pageSize = 4; //_platform.flashPageSize(); // align to 32bit for now, as aligning to flash-page-size causes side effects in programming + // pagesize should be a multiply of two + return (size + pageSize - 1) & (-1*pageSize); } MemoryBlock* Memory::findBlockInList(MemoryBlock* head, uint8_t* address) @@ -429,3 +486,13 @@ void Memory::addNewUsedBlock(uint8_t* address, size_t size) MemoryBlock* newUsedBlock = new MemoryBlock(address, size); addToUsedList(newUsedBlock); } + +void Memory::versionCheckCallback(VersionCheckCallback func) +{ + _versionCheckCallback = func; +} + +VersionCheckCallback Memory::versionCheckCallback() +{ + return _versionCheckCallback; +} diff --git a/src/knx/memory.h b/src/knx/memory.h index 962437f..58a10ef 100644 --- a/src/knx/memory.h +++ b/src/knx/memory.h @@ -24,12 +24,23 @@ class MemoryBlock MemoryBlock* next = nullptr; }; +enum VersionCheckResult +{ + FlashAllInvalid = 0, //!< All flash content is not valid for this firmware, we delete it + FlashTablesInvalid = 1,//!< All table objects are invalid for this firmware, device object and saveRestores are OK + FlashValid = 2 //!< Flash content is valid and will be used +}; + +typedef VersionCheckResult (*VersionCheckCallback)(uint16_t manufacturerId, uint8_t* hardwareType, uint16_t version); + class Memory { public: Memory(Platform& platform, DeviceObject& deviceObject); + virtual ~Memory(); void readMemory(); void writeMemory(); + void saveMemory(); void addSaveRestore(SaveRestore* obj); void addSaveRestore(TableObject* obj); @@ -39,6 +50,9 @@ public: uint8_t* toAbsolute(uint32_t relativeAddress); uint32_t toRelative(uint8_t* absoluteAddress); + void versionCheckCallback(VersionCheckCallback func); + VersionCheckCallback versionCheckCallback(); + private: void removeFromFreeList(MemoryBlock* block); void addToUsedList(MemoryBlock* block); @@ -49,14 +63,19 @@ public: MemoryBlock* findBlockInList(MemoryBlock* head, uint8_t* address); void addNewUsedBlock(uint8_t* address, size_t size); + void readEraseBlockToBuffer(uint32_t blockNum); + uint8_t* eraseBlockStart(uint32_t blockNum); + uint8_t* eraseBlockEnd(uint32_t blockNum); + void saveBufferdEraseBlock(); + + VersionCheckCallback _versionCheckCallback = 0; Platform& _platform; DeviceObject& _deviceObject; SaveRestore* _saveRestores[MAXSAVE] = {0}; TableObject* _tableObjects[MAXTABLEOBJ] = {0}; uint8_t _saveCount = 0; uint8_t _tableObjCount = 0; - uint8_t* _data = nullptr; MemoryBlock* _freeList = nullptr; MemoryBlock* _usedList = nullptr; - uint16_t _metadataSize = 4 + LEN_HARDWARE_TYPE; // accounting for 2x pushWord and pushByteArray of length LEN_HARDWARE_TYPE + uint16_t _metadataSize = 6 + LEN_HARDWARE_TYPE; // accounting for 3x pushWord and pushByteArray of length LEN_HARDWARE_TYPE }; diff --git a/src/knx/network_layer_coupler.h b/src/knx/network_layer_coupler.h index 47de6dc..b57f30a 100644 --- a/src/knx/network_layer_coupler.h +++ b/src/knx/network_layer_coupler.h @@ -27,10 +27,10 @@ class NetworkLayerCoupler : public NetworkLayer void rtObj(RouterObject& rtObj); // Coupler model 1.x // from transport layer - virtual void dataIndividualRequest(AckType ack, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu) override; - virtual void dataGroupRequest(AckType ack, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu) override; - virtual void dataBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu) override; - virtual void dataSystemBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu) override; + void dataIndividualRequest(AckType ack, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu) override; + void dataGroupRequest(AckType ack, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu) override; + void dataBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu) override; + void dataSystemBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu) override; private: enum CouplerType @@ -46,16 +46,16 @@ class NetworkLayerCoupler : public NetworkLayer static constexpr uint8_t kLocalIfIndex = 99; // from entities - virtual void dataIndication(AckType ack, AddressType addType, uint16_t destination, FrameFormat format, NPDU& npdu, + void dataIndication(AckType ack, AddressType addType, uint16_t destination, FrameFormat format, NPDU& npdu, Priority priority, uint16_t source, uint8_t srcIfIdx) override; - virtual void dataConfirm(AckType ack, AddressType addrType, uint16_t destination, FrameFormat format, Priority priority, + void dataConfirm(AckType ack, AddressType addrType, uint16_t destination, FrameFormat format, Priority priority, uint16_t source, NPDU& npdu, bool status, uint8_t srcIfIdx) override; - virtual void broadcastIndication(AckType ack, FrameFormat format, NPDU& npdu, + void broadcastIndication(AckType ack, FrameFormat format, NPDU& npdu, Priority priority, uint16_t source, uint8_t srcIfIdx) override; - virtual void broadcastConfirm(AckType ack, FrameFormat format, Priority priority, uint16_t source, NPDU& npdu, bool status, uint8_t srcIfIdx) override; - virtual void systemBroadcastIndication(AckType ack, FrameFormat format, NPDU& npdu, + void broadcastConfirm(AckType ack, FrameFormat format, Priority priority, uint16_t source, NPDU& npdu, bool status, uint8_t srcIfIdx) override; + void systemBroadcastIndication(AckType ack, FrameFormat format, NPDU& npdu, Priority priority, uint16_t source, uint8_t srcIfIdx) override; - virtual void systemBroadcastConfirm(AckType ack, FrameFormat format, Priority priority, uint16_t source, NPDU& npdu, bool status, uint8_t srcIfIdx) override; + void systemBroadcastConfirm(AckType ack, FrameFormat format, Priority priority, uint16_t source, NPDU& npdu, bool status, uint8_t srcIfIdx) override; void routeDataIndividual(AckType ack, uint16_t destination, NPDU& npdu, Priority priority, uint16_t source, uint8_t srcIfIndex); void sendMsgHopCount(AckType ack, AddressType addrType, uint16_t destination, NPDU& npdu, Priority priority, diff --git a/src/knx/network_layer_device.h b/src/knx/network_layer_device.h index ad7f70c..3fdd6b2 100644 --- a/src/knx/network_layer_device.h +++ b/src/knx/network_layer_device.h @@ -19,23 +19,23 @@ class NetworkLayerDevice : public NetworkLayer NetworkLayerEntity& getInterface(); // from transport layer - virtual void dataIndividualRequest(AckType ack, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu) override; - virtual void dataGroupRequest(AckType ack, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu) override; - virtual void dataBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu) override; - virtual void dataSystemBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu) override; + void dataIndividualRequest(AckType ack, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu) override; + void dataGroupRequest(AckType ack, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu) override; + void dataBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu) override; + void dataSystemBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu) override; private: // from entities - virtual void dataIndication(AckType ack, AddressType addType, uint16_t destination, FrameFormat format, NPDU& npdu, + void dataIndication(AckType ack, AddressType addType, uint16_t destination, FrameFormat format, NPDU& npdu, Priority priority, uint16_t source, uint8_t srcIfIdx) override; - virtual void dataConfirm(AckType ack, AddressType addrType, uint16_t destination, FrameFormat format, Priority priority, + void dataConfirm(AckType ack, AddressType addrType, uint16_t destination, FrameFormat format, Priority priority, uint16_t source, NPDU& npdu, bool status, uint8_t srcIfIdx) override; - virtual void broadcastIndication(AckType ack, FrameFormat format, NPDU& npdu, + void broadcastIndication(AckType ack, FrameFormat format, NPDU& npdu, Priority priority, uint16_t source, uint8_t srcIfIdx) override; - virtual void broadcastConfirm(AckType ack, FrameFormat format, Priority priority, uint16_t source, NPDU& npdu, bool status, uint8_t srcIfIdx) override; - virtual void systemBroadcastIndication(AckType ack, FrameFormat format, NPDU& npdu, + void broadcastConfirm(AckType ack, FrameFormat format, Priority priority, uint16_t source, NPDU& npdu, bool status, uint8_t srcIfIdx) override; + void systemBroadcastIndication(AckType ack, FrameFormat format, NPDU& npdu, Priority priority, uint16_t source, uint8_t srcIfIdx) override; - virtual void systemBroadcastConfirm(AckType ack, FrameFormat format, Priority priority, uint16_t source, NPDU& npdu, bool status, uint8_t srcIfIdx) override; + void systemBroadcastConfirm(AckType ack, FrameFormat format, Priority priority, uint16_t source, NPDU& npdu, bool status, uint8_t srcIfIdx) override; // Support only a single physical interface for normal devices NetworkLayerEntity _netLayerEntities[1]; diff --git a/src/knx/platform.cpp b/src/knx/platform.cpp index 1816960..a66a341 100644 --- a/src/knx/platform.cpp +++ b/src/knx/platform.cpp @@ -1,12 +1,15 @@ #include "platform.h" +#include "bits.h" + +#include +#include NvMemoryType Platform::NonVolatileMemoryType() { return _memoryType; } - void Platform::NonVolatileMemoryType(NvMemoryType type) { _memoryType = type; @@ -97,3 +100,155 @@ int Platform::readBytesMultiCast(uint8_t *buffer, uint16_t maxLen) { return 0; } + +size_t Platform::flashEraseBlockSize() +{ + return 0; +} + +size_t Platform::flashPageSize() +{ + // align to 32bit as default for Eeprom Emulation plattforms + return 4; +} + +uint8_t *Platform::userFlashStart() +{ + return nullptr; +} + +size_t Platform::userFlashSizeEraseBlocks() +{ + return 0; +} + +void Platform::flashErase(uint16_t eraseBlockNum) +{} + +void Platform::flashWritePage(uint16_t pageNumber, uint8_t* data) +{} + +uint8_t * Platform::getEepromBuffer(uint16_t size) +{ + return nullptr; +} + +void Platform::commitToEeprom() +{} + +uint8_t* Platform::getNonVolatileMemoryStart() +{ + if(_memoryType == Flash) + return userFlashStart(); + else + return getEepromBuffer(KNX_FLASH_SIZE); +} + +size_t Platform::getNonVolatileMemorySize() +{ + if(_memoryType == Flash) + return userFlashSizeEraseBlocks() * flashEraseBlockSize() * flashPageSize(); + else + return KNX_FLASH_SIZE; +} + +void Platform::commitNonVolatileMemory() +{ + if(_memoryType == Flash) + { + if(_bufferedEraseblockNumber > -1 && _bufferedEraseblockDirty) + { + writeBufferedEraseBlock(); + + free(_eraseblockBuffer); + _eraseblockBuffer = nullptr; + _bufferedEraseblockNumber = -1; // does that make sense? + } + } + else + { + commitToEeprom(); + } +} + +uint32_t Platform::writeNonVolatileMemory(uint32_t relativeAddress, uint8_t* buffer, size_t size) +{ + if(_memoryType == Flash) + { + while (size > 0) + { + loadEraseblockContaining(relativeAddress); + uint32_t start = _bufferedEraseblockNumber * (flashEraseBlockSize() * flashPageSize()); + uint32_t end = start + (flashEraseBlockSize() * flashPageSize()); + + ptrdiff_t offset = relativeAddress - start; + ptrdiff_t length = end - relativeAddress; + if(length > size) + length = size; + memcpy(_eraseblockBuffer + offset, buffer, length); + _bufferedEraseblockDirty = true; + + relativeAddress += length; + buffer += length; + size -= length; + } + return relativeAddress; + } + else + { + memcpy(getEepromBuffer(KNX_FLASH_SIZE)+relativeAddress, buffer, size); + return relativeAddress+size; + } +} + +void Platform::loadEraseblockContaining(uint32_t relativeAddress) +{ + int32_t blockNum = getEraseBlockNumberOf(relativeAddress); + if (blockNum < 0) + { + println("loadEraseblockContaining could not get valid eraseblock number"); + fatalError(); + } + + if (blockNum != _bufferedEraseblockNumber && _bufferedEraseblockNumber >= 0) + writeBufferedEraseBlock(); + + bufferEraseBlock(blockNum); +} + +int32_t Platform::getEraseBlockNumberOf(uint32_t relativeAddress) +{ + return relativeAddress / (flashEraseBlockSize() * flashPageSize()); +} + + +void Platform::writeBufferedEraseBlock() +{ + if(_bufferedEraseblockNumber > -1 && _bufferedEraseblockDirty) + { + flashErase(_bufferedEraseblockNumber); + for(int i = 0; i < flashEraseBlockSize(); i++) + { + int32_t pageNumber = _bufferedEraseblockNumber * flashEraseBlockSize() + i; + uint8_t *data = _eraseblockBuffer + flashPageSize() * i; + flashWritePage(pageNumber, data); + } + _bufferedEraseblockDirty = false; + } +} + + +void Platform::bufferEraseBlock(uint32_t eraseBlockNumber) +{ + if(_bufferedEraseblockNumber == eraseBlockNumber) + return; + + if(_eraseblockBuffer == nullptr) + { + _eraseblockBuffer = (uint8_t*)malloc(flashEraseBlockSize() * flashPageSize()); + } + memcpy(_eraseblockBuffer, userFlashStart() + eraseBlockNumber * flashEraseBlockSize() * flashPageSize(), flashEraseBlockSize() * flashPageSize()); + + _bufferedEraseblockNumber = eraseBlockNumber; + _bufferedEraseblockDirty = false; +} diff --git a/src/knx/platform.h b/src/knx/platform.h index 769083f..2aaf758 100644 --- a/src/knx/platform.h +++ b/src/knx/platform.h @@ -4,6 +4,11 @@ #include #include "save_restore.h" +#ifndef KNX_FLASH_SIZE +#define KNX_FLASH_SIZE 1024 +#pragma warning "KNX_FLASH_SIZE not defined, using 1024" +#endif + enum NvMemoryType { Eeprom, @@ -49,21 +54,62 @@ class Platform virtual void setupSpi(); virtual void closeSpi(); virtual int readWriteSpi(uint8_t *data, size_t len); -#if 0 - // Flash memory - virtual size_t flashEraseBlockSize(); // in pages - virtual size_t flashPageSize(); // in bytes - virtual uint8_t* userFlashStart(); // start of user flash aligned to start of an erase block - virtual size_t userFlashSizeEraseBlocks(); // in eraseBlocks - virtual void flashErase(uint16_t eraseBlockNum); //relativ to userFlashStart - virtual void flashWritePage(uint16_t pageNumber, uint8_t* data); //write a single page to flash (pageNumber relative to userFashStart -#endif - virtual uint8_t* getEepromBuffer(uint16_t size) = 0; - virtual void commitToEeprom() = 0; + + //Memory + + // --- Overwrite these methods in the device-plattform to use the EEPROM Emulation API for UserMemory ---- + // + // --- changes to the UserMemory are written directly into the address space starting at getEepromBuffer + // --- commitToEeprom must save this to a non-volatile area if neccessary + virtual uint8_t* getEepromBuffer(uint16_t size); + virtual void commitToEeprom(); + // ------------------------------------------------------------------------------------------------------- + + virtual uint8_t* getNonVolatileMemoryStart(); + virtual size_t getNonVolatileMemorySize(); + virtual void commitNonVolatileMemory(); + // address is relative to start of nonvolatile memory + virtual uint32_t writeNonVolatileMemory(uint32_t relativeAddress, uint8_t* buffer, size_t size); NvMemoryType NonVolatileMemoryType(); void NonVolatileMemoryType(NvMemoryType type); + // --- Overwrite these methods in the device-plattform to use flash memory handling by the knx stack --- + // --- also set _memoryType = Flash in the device-plattform's contructor + // --- optional: overwrite writeBufferedEraseBlock() in the device-plattform to reduce overhead when flashing multiple pages + + // size of one flash page in bytes + virtual size_t flashPageSize(); + protected: + // size of one EraseBlock in pages + virtual size_t flashEraseBlockSize(); + // start of user flash aligned to start of an erase block + virtual uint8_t* userFlashStart(); + // size of the user flash in EraseBlocks + virtual size_t userFlashSizeEraseBlocks(); + //relativ to userFlashStart + virtual void flashErase(uint16_t eraseBlockNum); + //write a single page to flash (pageNumber relative to userFashStart + virtual void flashWritePage(uint16_t pageNumber, uint8_t* data); + + + // ------------------------------------------------------------------------------------------------------- + + NvMemoryType _memoryType = Eeprom; + + void loadEraseblockContaining(uint32_t relativeAddress); + int32_t getEraseBlockNumberOf(uint32_t relativeAddress); + // writes _eraseblockBuffer to flash + virtual void writeBufferedEraseBlock(); + // copies a EraseBlock into the _eraseblockBuffer + void bufferEraseBlock(uint32_t eraseBlockNumber); + + // in theory we would have to use this buffer for memory reads too, + // but because ets always restarts the device after programming it + // we can ignore this issue + uint8_t* _eraseblockBuffer = nullptr; + int32_t _bufferedEraseblockNumber = -1; + bool _bufferedEraseblockDirty = false; }; \ No newline at end of file diff --git a/src/knx/rf_data_link_layer.h b/src/knx/rf_data_link_layer.h index cdbd9dc..79dd28b 100644 --- a/src/knx/rf_data_link_layer.h +++ b/src/knx/rf_data_link_layer.h @@ -27,7 +27,7 @@ class RfDataLinkLayer : public DataLinkLayer void loop(); void enabled(bool value); bool enabled() const; - virtual DptMedium mediumType() const override; + DptMedium mediumType() const override; private: bool _enabled = false; diff --git a/src/knx/rf_physical_layer_cc1101.cpp b/src/knx/rf_physical_layer_cc1101.cpp index c3de950..38134f2 100644 --- a/src/knx/rf_physical_layer_cc1101.cpp +++ b/src/knx/rf_physical_layer_cc1101.cpp @@ -679,7 +679,6 @@ void RfPhysicalLayerCC1101::loop() bytesLeft -= (32 - 1); pByteIndex += (32 - 1); } - } } @@ -719,7 +718,6 @@ void RfPhysicalLayerCC1101::loop() packetStartTime = millis(); syncStart = true; } - } } break; diff --git a/src/knx/rf_physical_layer_cc1310.h b/src/knx/rf_physical_layer_cc1310.h index 80f0bd4..4dbc2e6 100644 --- a/src/knx/rf_physical_layer_cc1310.h +++ b/src/knx/rf_physical_layer_cc1310.h @@ -26,9 +26,9 @@ class RfPhysicalLayerCC1310 : public RfPhysicalLayer public: RfPhysicalLayerCC1310(RfDataLinkLayer& rfDataLinkLayer, Platform& platform); - virtual bool InitChip() override; - virtual void stopChip() override; - virtual void loop() override; + bool InitChip() override; + void stopChip() override; + void loop() override; void setOutputPowerLevel(int8_t dBm); diff --git a/src/knx/router_object.h b/src/knx/router_object.h index e43838d..0064a06 100644 --- a/src/knx/router_object.h +++ b/src/knx/router_object.h @@ -34,12 +34,12 @@ public: bool isRfSbcRoutingEnabled(); bool isIpSbcRoutingEnabled(); - virtual void masterReset(EraseCode eraseCode, uint8_t channel) override; + void masterReset(EraseCode eraseCode, uint8_t channel) override; const uint8_t* restore(const uint8_t* buffer) override; protected: - virtual void beforeStateChange(LoadState& newState) override; + void beforeStateChange(LoadState& newState) override; private: // Function properties diff --git a/src/knx/secure_application_layer.h b/src/knx/secure_application_layer.h index 39b5fc5..5b213b3 100644 --- a/src/knx/secure_application_layer.h +++ b/src/knx/secure_application_layer.h @@ -21,7 +21,6 @@ class BusAccessUnit; */ class SecureApplicationLayer : public ApplicationLayer { - public: /** * The constructor. @@ -33,31 +32,29 @@ class SecureApplicationLayer : public ApplicationLayer void groupAddressTable(AddressTableObject& addrTable); // from transport layer - virtual void dataGroupIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu) override; - virtual void dataGroupConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, + void dataGroupIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu) override; + void dataGroupConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, bool status) override; - virtual void dataBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu) override; - virtual void dataBroadcastConfirm(AckType ack, HopCountType hopType, Priority priority, APDU& apdu, bool status) override; - virtual void dataSystemBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu) override; - virtual void dataSystemBroadcastConfirm(HopCountType hopType, Priority priority, APDU& apdu, bool status) override; - virtual void dataIndividualIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu) override; - virtual void dataIndividualConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, bool status) override; - virtual void dataConnectedIndication(Priority priority, uint16_t tsap, APDU& apdu) override; - virtual void dataConnectedConfirm(uint16_t tsap) override; + void dataBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu) override; + void dataBroadcastConfirm(AckType ack, HopCountType hopType, Priority priority, APDU& apdu, bool status) override; + void dataSystemBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu) override; + void dataSystemBroadcastConfirm(HopCountType hopType, Priority priority, APDU& apdu, bool status) override; + void dataIndividualIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu) override; + void dataIndividualConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, bool status) override; + void dataConnectedIndication(Priority priority, uint16_t tsap, APDU& apdu) override; + void dataConnectedConfirm(uint16_t tsap) override; void loop(); protected: // to transport layer - virtual void dataGroupRequest(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, const SecurityControl& secCtrl) override; - virtual void dataBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, APDU& apdu, const SecurityControl& secCtrl) override; - virtual void dataSystemBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, APDU& apdu, const SecurityControl& secCtrl) override; - virtual void dataIndividualRequest(AckType ack, HopCountType hopType, Priority priority, uint16_t destination, APDU& apdu, const SecurityControl& secCtrl) override; - virtual void dataConnectedRequest(uint16_t tsap, Priority priority, APDU& apdu, const SecurityControl& secCtrl) override; // apdu must be valid until it was confirmed + void dataGroupRequest(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, const SecurityControl& secCtrl) override; + void dataBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, APDU& apdu, const SecurityControl& secCtrl) override; + void dataSystemBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, APDU& apdu, const SecurityControl& secCtrl) override; + void dataIndividualRequest(AckType ack, HopCountType hopType, Priority priority, uint16_t destination, APDU& apdu, const SecurityControl& secCtrl) override; + void dataConnectedRequest(uint16_t tsap, Priority priority, APDU& apdu, const SecurityControl& secCtrl) override; // apdu must be valid until it was confirmed private: - - enum class AddrType : uint8_t { group, diff --git a/src/knx/security_interface_object.cpp b/src/knx/security_interface_object.cpp index e3feba1..bf0cdde 100644 --- a/src/knx/security_interface_object.cpp +++ b/src/knx/security_interface_object.cpp @@ -490,7 +490,6 @@ uint16_t SecurityInterfaceObject::getNumberOfElements(PropertyID propId) uint64_t SecurityInterfaceObject::getLastValidSequenceNumber(uint16_t deviceAddr) { - // Get number of entries for this property uint16_t numElements = getNumberOfElements(PID_SECURITY_INDIVIDUAL_ADDRESS_TABLE); diff --git a/src/knx/security_interface_object.h b/src/knx/security_interface_object.h index 53f5314..9a32896 100644 --- a/src/knx/security_interface_object.h +++ b/src/knx/security_interface_object.h @@ -11,7 +11,7 @@ class SecurityInterfaceObject: public InterfaceObject public: SecurityInterfaceObject(); - virtual void masterReset(EraseCode eraseCode, uint8_t channel) override; + void masterReset(EraseCode eraseCode, uint8_t channel) override; bool isSecurityModeEnabled(); diff --git a/src/knx/table_object.cpp b/src/knx/table_object.cpp index 909337a..1f670a9 100644 --- a/src/knx/table_object.cpp +++ b/src/knx/table_object.cpp @@ -6,6 +6,19 @@ #include "callback_property.h" #include "data_property.h" +BeforeTablesUnloadCallback TableObject::_beforeTablesUnload = 0; +uint8_t TableObject::_tableUnloadCount = 0; + +void TableObject::beforeTablesUnloadCallback(BeforeTablesUnloadCallback func) +{ + _beforeTablesUnload = func; +} + +BeforeTablesUnloadCallback TableObject::beforeTablesUnloadCallback() +{ + return _beforeTablesUnload; +} + TableObject::TableObject(Memory& memory) : _memory(memory) {} @@ -13,6 +26,19 @@ TableObject::TableObject(Memory& memory) TableObject::~TableObject() {} +void TableObject::beforeStateChange(LoadState& newState) +{ + if (newState == LS_LOADED && _tableUnloadCount > 0) + _tableUnloadCount--; + if (_tableUnloadCount > 0) + return; + if (newState == LS_UNLOADED) { + _tableUnloadCount++; + if (_beforeTablesUnload != 0) + _beforeTablesUnload(); + } +} + LoadState TableObject::loadState() { return _state; @@ -82,7 +108,11 @@ bool TableObject::allocTable(uint32_t size, bool doFill, uint8_t fillByte) return false; if (doFill) - memset(_data, fillByte, size); + { + uint32_t addr = _memory.toRelative(_data); + for(int i = 0; i< size;i++) + _memory.writeMemory(addr+i, 1, &fillByte); + } _size = size; @@ -139,6 +169,7 @@ void TableObject::loadEventLoading(const uint8_t* data) case LE_START_LOADING: break; case LE_LOAD_COMPLETED: + _memory.saveMemory(); loadState(LS_LOADED); break; case LE_UNLOAD: @@ -293,7 +324,6 @@ void TableObject::initializeProperties(size_t propertiesSize, Property** propert //TODO: missing // 23 PID_TABLE 3 / (3) - // 27 PID_MCB_TABLE 3 / 3 uint8_t ownPropertiesCount = sizeof(ownProperties) / sizeof(Property*); diff --git a/src/knx/table_object.h b/src/knx/table_object.h index c4213b3..66768cd 100644 --- a/src/knx/table_object.h +++ b/src/knx/table_object.h @@ -3,6 +3,9 @@ #include "interface_object.h" class Memory; + +typedef void (*BeforeTablesUnloadCallback)(); + /** * This class provides common functionality for interface objects that are configured by ETS with MemorWrite. */ @@ -28,14 +31,18 @@ class TableObject: public InterfaceObject uint8_t* save(uint8_t* buffer) override; const uint8_t* restore(const uint8_t* buffer) override; uint16_t saveSize() override; - protected: + + static void beforeTablesUnloadCallback(BeforeTablesUnloadCallback func); + static BeforeTablesUnloadCallback beforeTablesUnloadCallback(); + + protected: /** * This method is called before the interface object enters a new ::LoadState. * If there is a error changing the state newState should be set to ::LS_ERROR and errorCode() * to a reason for the failure. */ - virtual void beforeStateChange(LoadState& newState) {} - + virtual void beforeStateChange(LoadState& newState); + /** * returns the internal data of the interface object. This pointer belongs to the TableObject class and * must not be freed. @@ -47,7 +54,9 @@ class TableObject: public InterfaceObject void errorCode(ErrorCode errorCode); void initializeProperties(size_t propertiesSize, Property** properties) override; - + + static BeforeTablesUnloadCallback _beforeTablesUnload; + private: uint32_t tableReference(); bool allocTable(uint32_t size, bool doFill, uint8_t fillByte); @@ -68,6 +77,7 @@ class TableObject: public InterfaceObject LoadState _state = LS_UNLOADED; Memory& _memory; uint8_t *_data = 0; + static uint8_t _tableUnloadCount; /** * used to store size of data() in allocTable(), needed for calculation of crc in PID_MCB_TABLE. diff --git a/src/knx/tpuart_data_link_layer.cpp b/src/knx/tpuart_data_link_layer.cpp index ce6d5d7..1de2eb2 100644 --- a/src/knx/tpuart_data_link_layer.cpp +++ b/src/knx/tpuart_data_link_layer.cpp @@ -31,6 +31,9 @@ #define U_STOP_MODE_REQ 0x0E #define U_EXIT_STOP_MODE_REQ 0x0F #define U_ACK_REQ 0x10 //-0x17 +#define U_ACK_REQ_NACK 0x04 +#define U_ACK_REQ_BUSY 0x02 +#define U_ACK_REQ_ADRESSED 0x01 #define U_CONFIGURE_REQ 0x18 #define U_INT_REG_WR_REQ 0x28 #define U_INT_REG_RD_REQ 0x38 @@ -82,7 +85,6 @@ enum { TX_IDLE, TX_FRAME, - TX_WAIT_ECHO, TX_WAIT_CONN }; @@ -100,10 +102,23 @@ enum { #define RESET_TIMEOUT 100 //milli seconds #define TX_TIMEPAUSE 0 // 0 means 1 milli seconds +#define OVERRUN_COUNT 7 //bytes; max. allowed bytes in receive buffer (on start) to see it as overrun + // If this threshold is reached loop() goes into // "hog mode" where it stays in loop() while L2 address reception #define HOGMODE_THRESHOLD 3 // milli seconds +void TpUartDataLinkLayer::enterRxWaitEOP() +{ + // Flush input + while (_platform.uartAvailable()) + { + _platform.readUart(); + } + _lastByteRxTime = millis(); + _rxState = RX_WAIT_EOP; +} + void TpUartDataLinkLayer::loop() { if (!_enabled) @@ -122,7 +137,6 @@ void TpUartDataLinkLayer::loop() // Loop once and repeat as long we have rx data available do { // Signals to communicate from rx part with the tx part - bool isEchoComplete = false; // Flag that a complete echo is received uint8_t dataConnMsg = 0; // The DATA_CONN message just seen or 0 #ifdef KNX_WAIT_FOR_ADDR @@ -150,6 +164,12 @@ void TpUartDataLinkLayer::loop() case RX_WAIT_START: if (_platform.uartAvailable()) { + if (_platform.uartAvailable() > OVERRUN_COUNT) + { + print("input buffer overrun: "); println(_platform.uartAvailable()); + enterRxWaitEOP(); + break; + } rxByte = _platform.readUart(); #ifdef DBG_TRACE print(rxByte, HEX); @@ -258,7 +278,7 @@ void TpUartDataLinkLayer::loop() if (millis() - _lastByteRxTime > EOPR_TIMEOUT) { _rxState = RX_WAIT_START; - print("EOPR inside RX_L_ADDR"); + println("EOPR @ RX_L_ADDR"); break; } if (!_platform.uartAvailable()) @@ -274,27 +294,19 @@ void TpUartDataLinkLayer::loop() if (_RxByteCnt == 7) { //Destination Address + payload available - //check if echo - if (_sendBuffer != nullptr && (!((buffer[0] ^ _sendBuffer[0]) & ~0x20) && !memcmp(buffer + _convert + 1, _sendBuffer + 1, 5))) - { //ignore repeated bit of control byte - _isEcho = true; - } - else - { - _isEcho = false; - } + //check if echo; ignore repeat bit of control byte + _isEcho = (_sendBuffer != nullptr && (!((buffer[0] ^ _sendBuffer[0]) & ~0x20) && !memcmp(buffer + _convert + 1, _sendBuffer + 1, 5))); //convert into Extended.ind if (_convert) { - uint8_t payloadLength = buffer[6] & 0x0F; buffer[1] = buffer[6] & 0xF0; - buffer[6] = payloadLength; + buffer[6] &= 0x0F; } if (!_isEcho) { - uint8_t c = 0x10; + uint8_t c = U_ACK_REQ; // The bau knows everything and could either check the address table object (normal device) // or any filter tables (coupler) to see if we are addressed. @@ -305,11 +317,11 @@ void TpUartDataLinkLayer::loop() if (_cb.isAckRequired(addr, isGroupAddress)) { - c |= 0x01; + c |= U_ACK_REQ_ADRESSED; } // Hint: We can send directly here, this doesn't disturb other transmissions - // We don't have to update _lastByteTxTime because after ACK the timing is not so tight + // We don't have to update _lastByteTxTime because after U_ACK_REQ the timing is not so tight _platform.writeUart(c); } _rxState = RX_L_DATA; @@ -325,8 +337,8 @@ void TpUartDataLinkLayer::loop() #endif if (_RxByteCnt == MAX_KNX_TELEGRAM_SIZE) { - _rxState = RX_WAIT_EOP; println("invalid telegram size"); + enterRxWaitEOP(); } else { @@ -336,26 +348,25 @@ void TpUartDataLinkLayer::loop() if (_RxByteCnt == buffer[6] + 7 + 2) { //complete Frame received, payloadLength+1 for TCPI +1 for CRC + //check if crc is correct if (rxByte == (uint8_t)(~_xorSum)) { - //check if crc is correct - if (_isEcho && _sendBuffer != NULL) - { - //check if it is realy an echo, rx_crc = tx_crc - if (rxByte == _sendBuffer[_sendBufferLength - 1]) - _isEcho = true; - else - _isEcho = false; - } - if (_isEcho) - { - isEchoComplete = true; - } - else + if (!_isEcho) { _receiveBuffer[0] = 0x29; _receiveBuffer[1] = 0; +#ifdef DBG_TRACE + unsigned long runTime = millis(); +#endif frameBytesReceived(_receiveBuffer, _RxByteCnt + 2); +#ifdef DBG_TRACE + runTime = millis() - runTime; + if (runTime > (OVERRUN_COUNT*14)/10) + { + // complain when the runtime was long than the OVERRUN_COUNT allows + print("processing received frame took: "); print(runTime); println(" ms"); + } +#endif } _rxState = RX_WAIT_START; #ifdef DBG_TRACE @@ -365,7 +376,7 @@ void TpUartDataLinkLayer::loop() else { println("frame with invalid crc ignored"); - _rxState = RX_WAIT_EOP; + enterRxWaitEOP(); } } else @@ -376,20 +387,18 @@ void TpUartDataLinkLayer::loop() case RX_WAIT_EOP: if (millis() - _lastByteRxTime > EOP_TIMEOUT) { - _RxByteCnt = 0; + // found a gap _rxState = RX_WAIT_START; #ifdef DBG_TRACE println("RX_WAIT_START"); #endif break; } - if (!_platform.uartAvailable()) - break; - _lastByteRxTime = millis(); - rxByte = _platform.readUart(); -#ifdef DBG_TRACE - print(rxByte, HEX); -#endif + if (_platform.uartAvailable()) + { + _platform.readUart(); + _lastByteRxTime = millis(); + } break; default: break; @@ -397,8 +406,8 @@ void TpUartDataLinkLayer::loop() } while (_rxState == RX_L_ADDR && (stayInRx || _platform.uartAvailable())); // Check for spurios DATA_CONN message - if (dataConnMsg && _txState != TX_WAIT_CONN && _txState != TX_WAIT_ECHO) { - println("got unexpected L_DATA_CON"); + if (dataConnMsg && _txState != TX_WAIT_CONN) { + println("unexpected L_DATA_CON"); } switch (_txState) @@ -419,9 +428,9 @@ void TpUartDataLinkLayer::loop() if (sendSingleFrameByte() == false) { _waitConfirmStartTime = millis(); - _txState = TX_WAIT_ECHO; + _txState = TX_WAIT_CONN; #ifdef DBG_TRACE - println("TX_WAIT_ECHO"); + println("TX_WAIT_CONN"); #endif } else @@ -430,22 +439,10 @@ void TpUartDataLinkLayer::loop() } } break; - case TX_WAIT_ECHO: case TX_WAIT_CONN: - if (isEchoComplete) + if (dataConnMsg) { - _txState = TX_WAIT_CONN; -#ifdef DBG_TRACE - println("TX_WAIT_CONN"); -#endif - } - else if (dataConnMsg) - { - bool waitEcho = (_txState == TX_WAIT_ECHO); - if (waitEcho) { - println("L_DATA_CON without echo"); - } - dataConBytesReceived(_receiveBuffer, _RxByteCnt + 2, !waitEcho && ((dataConnMsg & SUCCESS) > 0)); + dataConBytesReceived(_receiveBuffer, _RxByteCnt + 2, (dataConnMsg & SUCCESS)); delete[] _sendBuffer; _sendBuffer = 0; _sendBufferLength = 0; @@ -579,7 +576,8 @@ DptMedium TpUartDataLinkLayer::mediumType() const bool TpUartDataLinkLayer::sendSingleFrameByte() { uint8_t cmd[2]; - uint8_t idx = _TxByteCnt / 64; + + uint8_t idx = _TxByteCnt >> 6; if (_sendBuffer == NULL) return false; @@ -594,9 +592,9 @@ bool TpUartDataLinkLayer::sendSingleFrameByte() } if (_TxByteCnt != _sendBufferLength - 1) - cmd[0] = U_L_DATA_START_CONT_REQ | _TxByteCnt; + cmd[0] = U_L_DATA_START_CONT_REQ | (_TxByteCnt & 0x3F); else - cmd[0] = U_L_DATA_END_REQ | _TxByteCnt; + cmd[0] = U_L_DATA_END_REQ | (_TxByteCnt & 0x3F); cmd[1] = _sendBuffer[_TxByteCnt]; #ifdef DBG_TRACE @@ -618,7 +616,6 @@ bool TpUartDataLinkLayer::sendSingleFrameByte() void TpUartDataLinkLayer::addFrameTxQueue(CemiFrame& frame) { - _tx_queue_frame_t* tx_frame = new _tx_queue_frame_t; tx_frame->length = frame.telegramLengthtTP(); tx_frame->data = new uint8_t[tx_frame->length]; diff --git a/src/knx/tpuart_data_link_layer.h b/src/knx/tpuart_data_link_layer.h index 5e78662..92332ad 100644 --- a/src/knx/tpuart_data_link_layer.h +++ b/src/knx/tpuart_data_link_layer.h @@ -27,7 +27,7 @@ class TpUartDataLinkLayer : public DataLinkLayer void loop(); void enabled(bool value); bool enabled() const; - virtual DptMedium mediumType() const override; + DptMedium mediumType() const override; private: bool _enabled = false; @@ -68,6 +68,7 @@ class TpUartDataLinkLayer : public DataLinkLayer bool sendFrame(CemiFrame& frame); void frameBytesReceived(uint8_t* buffer, uint16_t length); void dataConBytesReceived(uint8_t* buffer, uint16_t length, bool success); + void enterRxWaitEOP(); bool resetChip(); void stopChip(); diff --git a/src/knx/transport_layer.cpp b/src/knx/transport_layer.cpp index 1e3a7f8..78d7157 100644 --- a/src/knx/transport_layer.cpp +++ b/src/knx/transport_layer.cpp @@ -343,7 +343,6 @@ void TransportLayer::dataIndividualConfirm(AckType ack, uint16_t destination, Ho A5(destination); break; } - } break; case Disconnect: diff --git a/src/knx/usb_tunnel_interface.cpp b/src/knx/usb_tunnel_interface.cpp index b2e197f..83889ea 100644 --- a/src/knx/usb_tunnel_interface.cpp +++ b/src/knx/usb_tunnel_interface.cpp @@ -63,7 +63,6 @@ void UsbTunnelInterface::loop() handleHidReportRxQueue(); rxHaveCompletePacket = false; } - } /* USB TX */ @@ -136,7 +135,6 @@ void UsbTunnelInterface::loadNextTxFrame(uint8_t** sendBuffer, uint16_t* sendBuf } println(""); #endif - } void UsbTunnelInterface::sendKnxHidReport(ProtocolIdType protId, ServiceIdType servId, uint8_t* data, uint16_t length) diff --git a/src/knx_facade.cpp b/src/knx_facade.cpp index 5d48c7e..35a6a6b 100644 --- a/src/knx_facade.cpp +++ b/src/knx_facade.cpp @@ -15,35 +15,46 @@ #define ICACHE_RAM_ATTR #endif - ICACHE_RAM_ATTR void buttonUp() + #ifndef PROG_BTN_PRESS_MIN_MILLIS + #define PROG_BTN_PRESS_MIN_MILLIS 50 + #endif + + #ifndef PROG_BTN_PRESS_MAX_MILLIS + #define PROG_BTN_PRESS_MAX_MILLIS 500 + #endif + + + ICACHE_RAM_ATTR void buttonEvent() { - static uint32_t lastpressed=0; - if (millis() - lastpressed > 200){ + static uint32_t lastEvent=0; + uint32_t diff = millis() - lastEvent; + if (diff >= PROG_BTN_PRESS_MIN_MILLIS && diff <= PROG_BTN_PRESS_MAX_MILLIS){ knx.toggleProgMode(); - lastpressed = millis(); } + + lastEvent = millis(); } #endif #ifdef ARDUINO_ARCH_SAMD // predefined global instance for TP or RF or TP/RF coupler #if MASK_VERSION == 0x07B0 - KnxFacade knx(buttonUp); + KnxFacade knx(buttonEvent); #elif MASK_VERSION == 0x27B0 - KnxFacade knx(buttonUp); + KnxFacade knx(buttonEvent); #elif MASK_VERSION == 0x2920 - KnxFacade knx(buttonUp); + KnxFacade knx(buttonEvent); #else #error "Mask version not supported on ARDUINO_ARCH_SAMD" #endif #elif defined(ARDUINO_ARCH_RP2040) // predefined global instance for TP or RF or TP/RF coupler #if MASK_VERSION == 0x07B0 - KnxFacade knx(buttonUp); + KnxFacade knx(buttonEvent); #elif MASK_VERSION == 0x27B0 - KnxFacade knx(buttonUp); + KnxFacade knx(buttonEvent); #elif MASK_VERSION == 0x2920 - KnxFacade knx(buttonUp); + KnxFacade knx(buttonEvent); #else #error "Mask version not supported on ARDUINO_ARCH_RP2040" #endif @@ -51,11 +62,11 @@ #elif defined(ARDUINO_ARCH_ESP8266) // predefined global instance for TP or IP or TP/IP coupler #if MASK_VERSION == 0x07B0 - KnxFacade knx(buttonUp); + KnxFacade knx(buttonEvent); #elif MASK_VERSION == 0x57B0 - KnxFacade knx(buttonUp); + KnxFacade knx(buttonEvent); #elif MASK_VERSION == 0x091A - KnxFacade knx(buttonUp); + KnxFacade knx(buttonEvent); #else #error "Mask version not supported on ARDUINO_ARCH_ESP8266" #endif @@ -63,18 +74,18 @@ #elif defined(ARDUINO_ARCH_ESP32) // predefined global instance for TP or IP or TP/IP coupler #if MASK_VERSION == 0x07B0 - KnxFacade knx(buttonUp); + KnxFacade knx(buttonEvent); #elif MASK_VERSION == 0x57B0 - KnxFacade knx(buttonUp); + KnxFacade knx(buttonEvent); #elif MASK_VERSION == 0x091A - KnxFacade knx(buttonUp); + KnxFacade knx(buttonEvent); #else #error "Mask version not supported on ARDUINO_ARCH_ESP32" #endif #elif defined(ARDUINO_ARCH_STM32) #if MASK_VERSION == 0x07B0 - KnxFacade knx(buttonUp); + KnxFacade knx(buttonEvent); #else #error "Mask version not supported on ARDUINO_ARCH_STM32" #endif diff --git a/src/knx_facade.h b/src/knx_facade.h index ecfbda0..9d12182 100644 --- a/src/knx_facade.h +++ b/src/knx_facade.h @@ -48,13 +48,20 @@ #else #if !defined(LED_BUILTIN) #define LED_BUILTIN 5 // see GPIO_PinConfig gpioPinConfigs[] - #endif +#endif #include "cc1310_platform.h" #ifndef KNX_NO_AUTOMATIC_GLOBAL_INSTANCE extern void buttonUp(); #endif #endif +#ifndef KNX_LED + #define KNX_LED LED_BUILTIN +#endif +#ifndef KNX_BUTTON + #define KNX_BUTTON 0 +#endif + typedef const uint8_t* (*RestoreCallback)(const uint8_t* buffer); typedef uint8_t* (*SaveCallback)(uint8_t* buffer); typedef void (*IsrFunctionPtr)(); @@ -73,6 +80,7 @@ template class KnxFacade : private SaveRestore KnxFacade(B& bau) : _bau(bau) { + _platformPtr = static_cast(&bau.platform()); manufacturerId(0xfa); bauNumber(platform().uniqueSerialNumber()); _bau.addSaveRestore(this); @@ -176,24 +184,7 @@ template class KnxFacade : private SaveRestore _progLedOnCallback = progLedOnCallback; } - /** - * returns RISING if interrupt is created in a rising signal, FALLING otherwise - */ - uint32_t buttonPinInterruptOn() - { - return _buttonPinInterruptOn; - } - - /** - * Sets if the programming button creates a RISING or a FALLING signal. - * - * Set to RISING for GPIO--BUTTON--VDD or to FALLING for GPIO--BUTTON--GND - */ - void buttonPinInterruptOn(uint32_t value) - { - _buttonPinInterruptOn = value; - } - + uint32_t buttonPin() { return _buttonPin; @@ -280,9 +271,9 @@ template class KnxFacade : private SaveRestore { // Workaround for https://github.com/arduino/ArduinoCore-samd/issues/587 #if (ARDUINO_API_VERSION >= 10200) - attachInterrupt(_buttonPin, _progButtonISRFuncPtr, (PinStatus)_buttonPinInterruptOn); + attachInterrupt(_buttonPin, _progButtonISRFuncPtr, (PinStatus)CHANGE); #else - attachInterrupt(_buttonPin, _progButtonISRFuncPtr, _buttonPinInterruptOn); + attachInterrupt(_buttonPin, _progButtonISRFuncPtr, CHANGE); #endif } @@ -400,7 +391,18 @@ template class KnxFacade : private SaveRestore void restart(uint16_t individualAddress) { - _bau.restartRequest(individualAddress); + SecurityControl sc = {false, None}; + _bau.restartRequest(individualAddress, sc); + } + + void beforeRestartCallback(BeforeRestartCallback func) + { + _bau.beforeRestartCallback(func); + } + + BeforeRestartCallback beforeRestartCallback() + { + return _bau.beforeRestartCallback(); } private: @@ -410,9 +412,8 @@ template class KnxFacade : private SaveRestore ProgLedOnCallback _progLedOnCallback = 0; ProgLedOffCallback _progLedOffCallback = 0; uint32_t _ledPinActiveOn = LOW; - uint32_t _ledPin = LED_BUILTIN; - uint32_t _buttonPinInterruptOn = RISING; - uint32_t _buttonPin = 0; + uint32_t _ledPin = KNX_LED; + uint32_t _buttonPin = KNX_BUTTON; SaveCallback _saveCallback = 0; RestoreCallback _restoreCallback = 0; volatile bool _toggleProgMode = false; diff --git a/src/rp2040_arduino_platform.cpp b/src/rp2040_arduino_platform.cpp index 6ab9909..7392223 100644 --- a/src/rp2040_arduino_platform.cpp +++ b/src/rp2040_arduino_platform.cpp @@ -1,16 +1,22 @@ /*----------------------------------------------------- Plattform for Raspberry Pi Pico and other RP2040 boards +by SirSydom 2021-2022 made to work with arduino-pico - "Raspberry Pi Pico Arduino core, for all RP2040 boards" by Earl E. Philhower III https://github.com/earlephilhower/arduino-pico V1.11.0 -by SirSydom 2021-2022 RTTI must be set to enabled in the board options -A maximum of 4kB emulated EEPROM is supported. -For more, use or own emulation (maybe with littlefs) +Uses direct flash reading/writing. +Size ist defined by KNX_FLASH_SIZE (default 4k) - must be a multiple of 4096. +Offset in Flash is defined by KNX_FLASH_OFFSET (default 1,5MiB / 0x180000) - must be a multiple of 4096. + +EEPROM Emulation from arduino-pico core (max 4k) can be use by defining USE_RP2040_EEPROM_EMULATION + +A RAM-buffered Flash can be use by defining USE_RP2040_LARGE_EEPROM_EMULATION + ----------------------------------------------------*/ @@ -25,17 +31,37 @@ For more, use or own emulation (maybe with littlefs) #include // EEPROM emulation in flash, part of Earl E Philhowers Pi Pico Arduino support #include // from Pico SDK #include // from Pico SDK +#include // from Pico SDK +#define FLASHPTR ((uint8_t*)XIP_BASE + KNX_FLASH_OFFSET) + +#if KNX_FLASH_SIZE%4096 +#error "KNX_FLASH_SIZE must be multiple of 4096" +#endif + +#if KNX_FLASH_OFFSET%4096 +#error "KNX_FLASH_OFFSET must be multiple of 4096" +#endif + +#ifndef KNX_SERIAL +#define KNX_SERIAL Serial1 +#endif RP2040ArduinoPlatform::RP2040ArduinoPlatform() #ifndef KNX_NO_DEFAULT_UART - : ArduinoPlatform(&Serial1) + : ArduinoPlatform(&KNX_SERIAL) #endif { + #ifndef USE_RP2040_EEPROM_EMULATION + _memoryType = Flash; + #endif } RP2040ArduinoPlatform::RP2040ArduinoPlatform( HardwareSerial* s) : ArduinoPlatform(s) { + #ifndef USE_RP2040_EEPROM_EMULATION + _memoryType = Flash; + #endif } void RP2040ArduinoPlatform::setupUart() @@ -54,7 +80,14 @@ void RP2040ArduinoPlatform::setupUart() uint32_t RP2040ArduinoPlatform::uniqueSerialNumber() { pico_unique_board_id_t id; // 64Bit unique serial number from the QSPI flash - pico_get_unique_board_id(&id); + + noInterrupts(); + rp2040.idleOtherCore(); + + flash_get_unique_id(id.id); //pico_get_unique_board_id(&id); + + rp2040.resumeOtherCore(); + interrupts(); // use lower 4 byte and convert to unit32_t uint32_t uid = ((uint32_t)(id.id[4]) << 24) | ((uint32_t)(id.id[5]) << 16) | ((uint32_t)(id.id[6]) << 8) | (uint32_t)(id.id[7]); @@ -68,6 +101,47 @@ void RP2040ArduinoPlatform::restart() watchdog_reboot(0,0,0); } +#ifdef USE_RP2040_EEPROM_EMULATION + +#pragma warning "Using EEPROM Simulation" + +#ifdef USE_RP2040_LARGE_EEPROM_EMULATION + +uint8_t * RP2040ArduinoPlatform::getEepromBuffer(uint16_t size) +{ + if(size%4096) + { + println("KNX_FLASH_SIZE must be a multiple of 4096"); + fatalError(); + } + + if(!_rambuff_initialized) + { + memcpy(_rambuff, FLASHPTR, KNX_FLASH_SIZE); + _rambuff_initialized = true; + } + + return _rambuff; +} + +void RP2040ArduinoPlatform::commitToEeprom() +{ + noInterrupts(); + rp2040.idleOtherCore(); + + //ToDo: write block-by-block to prevent writing of untouched blocks + if(memcmp(_rambuff, FLASHPTR, KNX_FLASH_SIZE)) + { + flash_range_erase (KNX_FLASH_OFFSET, KNX_FLASH_SIZE); + flash_range_program(KNX_FLASH_OFFSET, _rambuff, KNX_FLASH_SIZE); + } + + rp2040.resumeOtherCore(); + interrupts(); +} + +#else + uint8_t * RP2040ArduinoPlatform::getEepromBuffer(uint16_t size) { if(size > 4096) @@ -91,6 +165,73 @@ void RP2040ArduinoPlatform::commitToEeprom() { EEPROM.commit(); } + +#endif + +#else + +size_t RP2040ArduinoPlatform::flashEraseBlockSize() +{ + return 16; // 16 pages x 256byte/page = 4096byte +} + +size_t RP2040ArduinoPlatform::flashPageSize() +{ + return 256; +} + +uint8_t* RP2040ArduinoPlatform::userFlashStart() +{ + return (uint8_t*)XIP_BASE + KNX_FLASH_OFFSET; +} + +size_t RP2040ArduinoPlatform::userFlashSizeEraseBlocks() +{ + if(KNX_FLASH_SIZE <= 0) + return 0; + else + return ( (KNX_FLASH_SIZE - 1) / (flashPageSize() * flashEraseBlockSize())) + 1; +} + +void RP2040ArduinoPlatform::flashErase(uint16_t eraseBlockNum) +{ + noInterrupts(); + rp2040.idleOtherCore(); + + flash_range_erase (KNX_FLASH_OFFSET + eraseBlockNum * flashPageSize() * flashEraseBlockSize(), flashPageSize() * flashEraseBlockSize()); + + rp2040.resumeOtherCore(); + interrupts(); +} + +void RP2040ArduinoPlatform::flashWritePage(uint16_t pageNumber, uint8_t* data) +{ + noInterrupts(); + rp2040.idleOtherCore(); + + flash_range_program(KNX_FLASH_OFFSET + pageNumber * flashPageSize(), data, flashPageSize()); + + rp2040.resumeOtherCore(); + interrupts(); +} + +void RP2040ArduinoPlatform::writeBufferedEraseBlock() +{ + if(_bufferedEraseblockNumber > -1 && _bufferedEraseblockDirty) + { + noInterrupts(); + rp2040.idleOtherCore(); + + flash_range_erase (KNX_FLASH_OFFSET + _bufferedEraseblockNumber * flashPageSize() * flashEraseBlockSize(), flashPageSize() * flashEraseBlockSize()); + flash_range_program(KNX_FLASH_OFFSET + _bufferedEraseblockNumber * flashPageSize() * flashEraseBlockSize(), _eraseblockBuffer, flashPageSize() * flashEraseBlockSize()); + + rp2040.resumeOtherCore(); + interrupts(); + + _bufferedEraseblockDirty = false; + } +} +#endif #endif diff --git a/src/rp2040_arduino_platform.h b/src/rp2040_arduino_platform.h index 75cc7ff..8a9e7f3 100644 --- a/src/rp2040_arduino_platform.h +++ b/src/rp2040_arduino_platform.h @@ -4,6 +4,16 @@ #ifdef ARDUINO_ARCH_RP2040 +#ifndef KNX_FLASH_OFFSET +#define KNX_FLASH_OFFSET 0x180000 // 1.5MiB +#pragma warning "KNX_FLASH_OFFSET not defined, using 0x180000" +#endif + +#ifdef USE_RP2040_LARGE_EEPROM_EMULATION +#define USE_RP2040_EEPROM_EMULATION +#endif + + class RP2040ArduinoPlatform : public ArduinoPlatform { public: @@ -13,11 +23,36 @@ public: void setupUart(); // unique serial number - uint32_t uniqueSerialNumber() override; + uint32_t uniqueSerialNumber() override; void restart(); + + #ifdef USE_RP2040_EEPROM_EMULATION uint8_t* getEepromBuffer(uint16_t size); void commitToEeprom(); + + #ifdef USE_RP2040_LARGE_EEPROM_EMULATION + uint8_t _rambuff[KNX_FLASH_SIZE]; + bool _rambuff_initialized = false; + #endif + #else + + // size of one EraseBlock in pages + virtual size_t flashEraseBlockSize(); + // size of one flash page in bytes + virtual size_t flashPageSize(); + // start of user flash aligned to start of an erase block + virtual uint8_t* userFlashStart(); + // size of the user flash in EraseBlocks + virtual size_t userFlashSizeEraseBlocks(); + //relativ to userFlashStart + virtual void flashErase(uint16_t eraseBlockNum); + //write a single page to flash (pageNumber relative to userFashStart + virtual void flashWritePage(uint16_t pageNumber, uint8_t* data); + + // writes _eraseblockBuffer to flash - overrides Plattform::writeBufferedEraseBlock() for performance optimization only + void writeBufferedEraseBlock(); + #endif }; #endif diff --git a/src/samd_platform.cpp b/src/samd_platform.cpp index f370a33..fc85645 100644 --- a/src/samd_platform.cpp +++ b/src/samd_platform.cpp @@ -4,17 +4,33 @@ #include #include +#ifdef USE_SAMD_EEPROM_EMULATION #include +#endif + +#if KNX_FLASH_SIZE % 1024 +#error "KNX_FLASH_SIZE must be multiple of 1024" +#endif + +#ifndef KNX_SERIAL +#define KNX_SERIAL Serial1 +#endif SamdPlatform::SamdPlatform() #ifndef KNX_NO_DEFAULT_UART - : ArduinoPlatform(&Serial1) + : ArduinoPlatform(&KNX_SERIAL) #endif { +#ifndef USE_SAMD_EEPROM_EMULATION + init(); +#endif } SamdPlatform::SamdPlatform( HardwareSerial* s) : ArduinoPlatform(s) { +#ifndef USE_SAMD_EEPROM_EMULATION + init(); +#endif } uint32_t SamdPlatform::uniqueSerialNumber() @@ -43,7 +59,9 @@ void SamdPlatform::restart() NVIC_SystemReset(); } -uint8_t * SamdPlatform::getEepromBuffer(uint16_t size) +#ifdef USE_SAMD_EEPROM_EMULATION +#pragma warning "Using EEPROM Simulation" +uint8_t* SamdPlatform::getEepromBuffer(uint16_t size) { //EEPROM.begin(size); if(size > EEPROM_EMULATION_SIZE) @@ -56,6 +74,156 @@ void SamdPlatform::commitToEeprom() { EEPROM.commit(); } +#else + +extern uint32_t __etext; +extern uint32_t __data_start__; +extern uint32_t __data_end__; + +static const uint32_t pageSizes[] = {8, 16, 32, 64, 128, 256, 512, 1024}; + +void SamdPlatform::init() +{ + _memoryType = Flash; + _pageSize = pageSizes[NVMCTRL->PARAM.bit.PSZ]; + _pageCnt = NVMCTRL->PARAM.bit.NVMP; + _rowSize = PAGES_PER_ROW * _pageSize; + + // find end of program flash and set limit to next row + uint32_t endEddr = (uint32_t)(&__etext + (&__data_end__ - &__data_start__)); // text + data MemoryBlock + _MemoryStart = getRowAddr(_pageSize * _pageCnt - KNX_FLASH_SIZE - 1); // 23295 + _MemoryEnd = getRowAddr(_pageSize * _pageCnt - 1); + // chosen flash size is not available anymore + if (_MemoryStart < endEddr) { + println("KNX_FLASH_SIZE is not available (possible too much flash use by firmware)"); + fatalError(); + } +} + +size_t SamdPlatform::flashEraseBlockSize() +{ + return PAGES_PER_ROW; +} + +size_t SamdPlatform::flashPageSize() +{ + return _pageSize; +} + +uint8_t* SamdPlatform::userFlashStart() +{ + return (uint8_t*)_MemoryStart; +} + +size_t SamdPlatform::userFlashSizeEraseBlocks() +{ + if (KNX_FLASH_SIZE <= 0) + return 0; + else + return ((KNX_FLASH_SIZE - 1) / (flashPageSize() * flashEraseBlockSize())) + 1; +} + +void SamdPlatform::flashErase(uint16_t eraseBlockNum) +{ + noInterrupts(); + + eraseRow((void *)(_MemoryStart + eraseBlockNum * _rowSize)); + // flash_range_erase(KNX_FLASH_OFFSET + eraseBlockNum * flashPageSize() * flashEraseBlockSize(), flashPageSize() * flashEraseBlockSize()); + + interrupts(); +} + +void SamdPlatform::flashWritePage(uint16_t pageNumber, uint8_t* data) +{ + noInterrupts(); + + write((void *)(_MemoryStart + pageNumber * _pageSize), data, _pageSize); + // flash_range_program(KNX_FLASH_OFFSET + pageNumber * flashPageSize(), data, flashPageSize()); + + interrupts(); +} + +void SamdPlatform::writeBufferedEraseBlock() +{ + if (_bufferedEraseblockNumber > -1 && _bufferedEraseblockDirty) + { + noInterrupts(); + + eraseRow((void *)(_MemoryStart + _bufferedEraseblockNumber * _rowSize)); + write((void *)(_MemoryStart + _bufferedEraseblockNumber * _rowSize), _eraseblockBuffer, _rowSize); + // flash_range_erase(KNX_FLASH_OFFSET + _bufferedEraseblockNumber * flashPageSize() * flashEraseBlockSize(), flashPageSize() * flashEraseBlockSize()); + // flash_range_program(KNX_FLASH_OFFSET + _bufferedEraseblockNumber * flashPageSize() * flashEraseBlockSize(), _eraseblockBuffer, flashPageSize() * flashEraseBlockSize()); + + interrupts(); + + _bufferedEraseblockDirty = false; + } +} + +uint32_t SamdPlatform::getRowAddr(uint32_t flasAddr) +{ + return flasAddr & ~(_rowSize - 1); +} + +void SamdPlatform::write(const volatile void *flash_ptr, const void *data, uint32_t size) +{ + // Calculate data boundaries + size = (size + 3) / 4; + volatile uint32_t *src_addr = (volatile uint32_t *)data; + volatile uint32_t *dst_addr = (volatile uint32_t *)flash_ptr; + // volatile uint32_t *dst_addr = (volatile uint32_t *)flash_ptr; + // const uint8_t *src_addr = (uint8_t *)data; + + // Disable automatic page write + NVMCTRL->CTRLB.bit.MANW = 1; + + // Do writes in pages + while (size) + { + // Execute "PBC" Page Buffer Clear + NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_PBC; + while (NVMCTRL->INTFLAG.bit.READY == 0) + { + } + + // Fill page buffer + uint32_t i; + for (i = 0; i < (_pageSize / 4) && size; i++) + { + *dst_addr = *src_addr; + src_addr++; + dst_addr++; + size--; + } + + // Execute "WP" Write Page + NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_WP; + while (NVMCTRL->INTFLAG.bit.READY == 0) + { + } + } +} + +void SamdPlatform::erase(const volatile void *flash_ptr, uint32_t size) +{ + const uint8_t *ptr = (const uint8_t *)flash_ptr; + while (size > _rowSize) + { + eraseRow(ptr); + ptr += _rowSize; + size -= _rowSize; + } + eraseRow(ptr); +} + +void SamdPlatform::eraseRow(const volatile void *flash_ptr) +{ + NVMCTRL->ADDR.reg = ((uint32_t)flash_ptr) / 2; + NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER; + while (!NVMCTRL->INTFLAG.bit.READY) + { + } +} + +#endif #endif - - diff --git a/src/samd_platform.h b/src/samd_platform.h index dd1765d..fb22dc6 100644 --- a/src/samd_platform.h +++ b/src/samd_platform.h @@ -4,6 +4,8 @@ #ifdef ARDUINO_ARCH_SAMD +#define PAGES_PER_ROW 4 + class SamdPlatform : public ArduinoPlatform { public: @@ -14,8 +16,40 @@ public: uint32_t uniqueSerialNumber() override; void restart(); +#ifdef USE_SAMD_EEPROM_EMULATION uint8_t* getEepromBuffer(uint16_t size); void commitToEeprom(); +#else + // size of one EraseBlock in pages + virtual size_t flashEraseBlockSize(); + // size of one flash page in bytes + virtual size_t flashPageSize(); + // start of user flash aligned to start of an erase block + virtual uint8_t* userFlashStart(); + // size of the user flash in EraseBlocks + virtual size_t userFlashSizeEraseBlocks(); + // relativ to userFlashStart + virtual void flashErase(uint16_t eraseBlockNum); + // write a single page to flash (pageNumber relative to userFashStart + virtual void flashWritePage(uint16_t pageNumber, uint8_t* data); + + // writes _eraseblockBuffer to flash - overrides Plattform::writeBufferedEraseBlock() for performance optimization only + void writeBufferedEraseBlock(); + +private: + void init(); + uint32_t _MemoryEnd = 0; + uint32_t _MemoryStart = 0; + uint32_t _pageSize; + uint32_t _rowSize; + uint32_t _pageCnt; + + uint32_t getRowAddr(uint32_t flasAddr); + void write(const volatile void* flash_ptr, const void* data, uint32_t size); + void erase(const volatile void* flash_ptr, uint32_t size); + void eraseRow(const volatile void* flash_ptr); + +#endif }; #endif diff --git a/src/stm32_platform.cpp b/src/stm32_platform.cpp index 8fbfaa3..fd6ea37 100644 --- a/src/stm32_platform.cpp +++ b/src/stm32_platform.cpp @@ -4,9 +4,13 @@ #include #include "knx/bits.h" +#ifndef KNX_SERIAL +#define KNX_SERIAL Serial2 +#endif + Stm32Platform::Stm32Platform() #ifndef KNX_NO_DEFAULT_UART - : ArduinoPlatform(&Serial2) + : ArduinoPlatform(&KNX_SERIAL) #endif { } @@ -32,16 +36,21 @@ void Stm32Platform::restart() uint8_t * Stm32Platform::getEepromBuffer(uint16_t size) { - if (size > E2END + 1) + // check if the buffer already exists + if (_eepromPtr == nullptr) // we need to initialize the buffer first { - fatalError(); + if (size > E2END + 1) + { + fatalError(); + } + + _eepromSize = size; + _eepromPtr = new uint8_t[size]; + eeprom_buffer_fill(); + for (uint16_t i = 0; i < size; ++i) + _eepromPtr[i] = eeprom_buffered_read_byte(i); } - _eepromSize = size; - delete [] _eepromPtr; - _eepromPtr = new uint8_t[size]; - eeprom_buffer_fill(); - for (uint16_t i = 0; i < size; ++i) - _eepromPtr[i] = eeprom_buffered_read_byte(i); + return _eepromPtr; } @@ -51,6 +60,11 @@ void Stm32Platform::commitToEeprom() return; for (uint16_t i = 0; i < _eepromSize; ++i) eeprom_buffered_write_byte(i, _eepromPtr[i]); + // For some GD32 chips, the flash needs to be unlocked twice + // and the first call will fail. If the first call is + // successful, the second one (inside eeprom_buffer_flush) + // does nothing. + HAL_FLASH_Unlock(); eeprom_buffer_flush(); }