mirror of
https://github.com/thelsing/knx.git
synced 2025-08-03 13:48:15 +02:00
Merge pull request #215 from thelsing/master
merge master into feature branch
This commit is contained in:
commit
376ab5c84b
8
.github/workflows/cmake-arm.yml
vendored
8
.github/workflows/cmake-arm.yml
vendored
@ -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
|
||||
|
6
.github/workflows/cmake-native.yml
vendored
6
.github/workflows/cmake-native.yml
vendored
@ -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
|
||||
|
2
.github/workflows/codeql-analysis.yml
vendored
2
.github/workflows/codeql-analysis.yml
vendored
@ -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
|
||||
|
||||
|
@ -8,8 +8,6 @@ For ESP8266 and ESP32 [WifiManager](https://github.com/tzapu/WiFiManager) is use
|
||||
|
||||
Don't forget to reset ESP8266 manually (disconnect power) after flashing. The reboot doen't work during configuration with ETS otherwise.
|
||||
|
||||
The SAMD21 version uses my version of the [FlashStorage](https://github.com/thelsing/FlashStorage) lib (Pull request pending).
|
||||
|
||||
Generated documentation can be found [here](https://knx.readthedocs.io/en/latest/).
|
||||
|
||||
## Stack configuration possibilities
|
||||
@ -52,4 +50,4 @@ void main ()
|
||||
}
|
||||
```
|
||||
|
||||
More configuration options can be found in the examples.
|
||||
More configuration options can be found in the examples.
|
||||
|
@ -15,7 +15,6 @@ board = adafruit_feather_m0
|
||||
framework = arduino
|
||||
lib_deps =
|
||||
SPI
|
||||
https://github.com/thelsing/FlashStorage.git
|
||||
knx
|
||||
|
||||
build_flags =
|
||||
|
@ -24,7 +24,6 @@ lib_extra_dirs = ../../../
|
||||
|
||||
lib_deps =
|
||||
SPI
|
||||
https://github.com/thelsing/FlashStorage.git
|
||||
knx
|
||||
|
||||
build_flags =
|
||||
|
@ -15,7 +15,6 @@ board = adafruit_feather_m0
|
||||
framework = arduino
|
||||
lib_deps =
|
||||
SPI
|
||||
https://github.com/thelsing/FlashStorage.git
|
||||
knx
|
||||
|
||||
build_flags =
|
||||
|
@ -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
|
||||
|
210
examples/knx-h8i8o/knx-h8i8o.ino
Normal file
210
examples/knx-h8i8o/knx-h8i8o.ino
Normal file
@ -0,0 +1,210 @@
|
||||
#include <Arduino.h>
|
||||
#include <knx.h>
|
||||
|
||||
#define NUMIOS 18
|
||||
|
||||
#define goInputOnOff(i) knx.getGroupObject(i + 1)
|
||||
#define goOutputOnOff(i) knx.getGroupObject(i + 1 + NUMIOS)
|
||||
#define goOutputScaling(i) knx.getGroupObject(i + 1 + NUMIOS * 2)
|
||||
#define goOutputOnOffStatus(i) knx.getGroupObject(i + 1 + NUMIOS * 3)
|
||||
#define goOutputScalingStatus(i) knx.getGroupObject(i + 1 + NUMIOS * 4)
|
||||
|
||||
typedef enum {
|
||||
CONFIG_NONE = 0,
|
||||
CONFIG_IN = 1,
|
||||
CONFIG_IN_TOGGLE = 2,
|
||||
CONFIG_IN_TOGGLE_ON = 3,
|
||||
CONFIG_OUT = 4,
|
||||
CONFIG_OUT_ON = 5,
|
||||
CONFIG_OUT_PWM = 6,
|
||||
CONFIG_OUT_PWM_ON = 7,
|
||||
} config_t;
|
||||
|
||||
inline void isrIn(uint8_t pin, uint8_t toggle);
|
||||
|
||||
// workaround for the weird Arduino interrupt handling
|
||||
#define ISR_IN(NUM, TOGGLE) void isrIn##NUM##_##TOGGLE(){isrIn(NUM, TOGGLE);}
|
||||
ISR_IN(0,0);
|
||||
ISR_IN(1,0);
|
||||
ISR_IN(2,0);
|
||||
ISR_IN(3,0);
|
||||
ISR_IN(4,0);
|
||||
ISR_IN(5,0);
|
||||
ISR_IN(6,0);
|
||||
ISR_IN(7,0);
|
||||
ISR_IN(8,0);
|
||||
ISR_IN(9,0);
|
||||
ISR_IN(10,0);
|
||||
ISR_IN(11,0);
|
||||
ISR_IN(12,0);
|
||||
ISR_IN(13,0);
|
||||
ISR_IN(14,0);
|
||||
ISR_IN(15,0);
|
||||
ISR_IN(16,0);
|
||||
ISR_IN(17,0);
|
||||
ISR_IN(0,1);
|
||||
ISR_IN(1,1);
|
||||
ISR_IN(2,1);
|
||||
ISR_IN(3,1);
|
||||
ISR_IN(4,1);
|
||||
ISR_IN(5,1);
|
||||
ISR_IN(6,1);
|
||||
ISR_IN(7,1);
|
||||
ISR_IN(8,1);
|
||||
ISR_IN(9,1);
|
||||
ISR_IN(10,1);
|
||||
ISR_IN(11,1);
|
||||
ISR_IN(12,1);
|
||||
ISR_IN(13,1);
|
||||
ISR_IN(14,1);
|
||||
ISR_IN(15,1);
|
||||
ISR_IN(16,1);
|
||||
ISR_IN(17,1);
|
||||
|
||||
const uint32_t pinTbl[NUMIOS] = {PA15, PB3, PB4, PB5, PB6, PB7, PB8, PB9, PC13, PA0, PA1, PA2, PA3, PA4, PA5, PA6, PA7, PB0};
|
||||
void (*inCbTbl[NUMIOS * 2])(void) = {isrIn0_0, isrIn1_0, isrIn2_0, isrIn3_0, isrIn4_0, isrIn5_0, isrIn6_0, isrIn7_0, isrIn8_0, isrIn9_0, isrIn10_0, isrIn11_0, isrIn12_0, isrIn13_0, isrIn14_0, isrIn15_0, isrIn16_0, isrIn17_0, isrIn0_1, isrIn1_1, isrIn2_1, isrIn3_1, isrIn4_1, isrIn5_1, isrIn6_1, isrIn7_1, isrIn8_1, isrIn9_1, isrIn10_1, isrIn11_1, isrIn12_1, isrIn13_1, isrIn14_1, isrIn15_1, isrIn16_1, isrIn17_1};
|
||||
volatile uint16_t val[NUMIOS];
|
||||
uint32_t lastEvent[NUMIOS];
|
||||
|
||||
// callback for OnOff events from KNX
|
||||
void outOnOff(GroupObject& go)
|
||||
{
|
||||
uint8_t pin = go.asap() - 1 - NUMIOS;
|
||||
val[pin] = go.value();
|
||||
val[pin] &= 1;
|
||||
digitalWrite(pinTbl[pin], val[pin]);
|
||||
goOutputOnOffStatus(pin).value(val[pin]);
|
||||
}
|
||||
|
||||
// callback for OnOff events from KNX on PWM pins
|
||||
void outOnOffPWM(GroupObject& go)
|
||||
{
|
||||
uint8_t pin = go.asap() - 1 - NUMIOS;
|
||||
uint8_t tmp;
|
||||
if(go.value()){
|
||||
val[pin] |= 0x100;
|
||||
analogWrite(pinTbl[pin], val[pin] & 0xff);
|
||||
}else{
|
||||
val[pin] &= 0xff;
|
||||
analogWrite(pinTbl[pin], 0);
|
||||
}
|
||||
tmp = val[pin] >> 8;
|
||||
goOutputOnOffStatus(pin).value(tmp);
|
||||
}
|
||||
|
||||
// callback for 0-100% events from KNX
|
||||
void outScaling(GroupObject& go)
|
||||
{
|
||||
uint8_t pin = go.asap() - NUMIOS * 2 - 1;
|
||||
uint8_t tmp;
|
||||
if(val[pin] > 0xFF){
|
||||
tmp = *go.valueRef();
|
||||
val[pin] = tmp | 1 << 8;
|
||||
analogWrite(pinTbl[pin], val[pin] & 0xff);
|
||||
}else{
|
||||
tmp = *go.valueRef();
|
||||
val[pin] = tmp;
|
||||
}
|
||||
*goOutputScalingStatus(pin).valueRef() = tmp;
|
||||
goOutputScalingStatus(pin).objectWritten();
|
||||
}
|
||||
|
||||
// callback for input interrupts
|
||||
inline void isrIn(uint8_t pin, uint8_t toggle)
|
||||
{
|
||||
uint32_t diff = millis() - lastEvent[pin];
|
||||
if (diff >= 50 && diff <= 500){
|
||||
if(toggle){
|
||||
val[pin]++;
|
||||
}else{
|
||||
val[pin] = digitalRead(pinTbl[pin]);
|
||||
}
|
||||
val[pin] &= 1;
|
||||
goInputOnOff(pin).value(val[pin]);
|
||||
}
|
||||
|
||||
lastEvent[pin] = millis();
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
// read adress table, association table, groupobject table and parameters from eeprom
|
||||
knx.readMemory();
|
||||
|
||||
// print values of parameters if device is already configured
|
||||
if (knx.configured())
|
||||
{
|
||||
uint8_t progIn = knx.paramByte(0); // programming input
|
||||
uint8_t progLed = knx.paramByte(1); // programming LED
|
||||
|
||||
for(uint8_t i = 0; i < NUMIOS; i++){
|
||||
config_t config = (config_t) knx.paramByte(i + 2);
|
||||
// loop through all the pins and configure them correctly
|
||||
switch(config){
|
||||
case CONFIG_IN:
|
||||
pinMode(pinTbl[i], INPUT_PULLUP);
|
||||
if(progIn = i + 1){
|
||||
knx.buttonPin(pinTbl[i]);
|
||||
}else{
|
||||
goInputOnOff(i).dataPointType(DPT_Switch);
|
||||
#if (ARDUINO_API_VERSION >= 10200)
|
||||
attachInterrupt(digitalPinToInterrupt(pinTbl[i]), inCbTbl[i], (PinStatus)CHANGE);
|
||||
#else
|
||||
attachInterrupt(digitalPinToInterrupt(pinTbl[i]), inCbTbl[i], CHANGE);
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case CONFIG_IN_TOGGLE:
|
||||
case CONFIG_IN_TOGGLE_ON:
|
||||
goInputOnOff(i).dataPointType(DPT_Switch);
|
||||
val[i] = config == CONFIG_IN_TOGGLE_ON;
|
||||
pinMode(pinTbl[i], INPUT_PULLUP);
|
||||
#if (ARDUINO_API_VERSION >= 10200)
|
||||
attachInterrupt(digitalPinToInterrupt(pinTbl[i]), inCbTbl[i + NUMIOS], (PinStatus)CHANGE);
|
||||
#else
|
||||
attachInterrupt(digitalPinToInterrupt(pinTbl[i]), inCbTbl[i + NUMIOS], CHANGE);
|
||||
#endif
|
||||
break;
|
||||
case CONFIG_OUT:
|
||||
case CONFIG_OUT_ON:
|
||||
pinMode(pinTbl[i], OUTPUT);
|
||||
val[i] = config == CONFIG_OUT_ON;
|
||||
digitalWrite(pinTbl[i], val[i]);
|
||||
if(progLed = i + 1){
|
||||
knx.ledPin(pinTbl[i]);
|
||||
}else{
|
||||
goOutputOnOff(i).dataPointType(DPT_Switch);
|
||||
goOutputOnOff(i).callback(outOnOff);
|
||||
goOutputOnOffStatus(i).dataPointType(DPT_Switch);
|
||||
}
|
||||
break;
|
||||
case CONFIG_OUT_PWM:
|
||||
case CONFIG_OUT_PWM_ON:
|
||||
pinMode(pinTbl[i], OUTPUT);
|
||||
val[i] = config == CONFIG_OUT_PWM_ON ? 0 : 0x1ff;
|
||||
analogWrite(pinTbl[i], val[i] & 0xff);
|
||||
goOutputOnOff(i).dataPointType(DPT_Switch);
|
||||
goOutputOnOff(i).callback(outOnOffPWM);
|
||||
goOutputOnOffStatus(i).dataPointType(DPT_Switch);
|
||||
goOutputScaling(i).dataPointType(DPT_Scaling);
|
||||
goOutputScaling(i).callback(outScaling);
|
||||
goOutputScalingStatus(i).dataPointType(DPT_Scaling);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// start the framework.
|
||||
knx.start();
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// don't delay here too much. Otherwise you might lose packages or mess up the timing with ETS
|
||||
knx.loop();
|
||||
|
||||
// only run the application code if the device was configured with ETS
|
||||
if (!knx.configured())
|
||||
return;
|
||||
}
|
BIN
examples/knx-h8i8o/knx-h8i8o.knxprod
Normal file
BIN
examples/knx-h8i8o/knx-h8i8o.knxprod
Normal file
Binary file not shown.
2198
examples/knx-h8i8o/knx-h8i8o.xml
Normal file
2198
examples/knx-h8i8o/knx-h8i8o.xml
Normal file
File diff suppressed because it is too large
Load Diff
39
examples/knx-h8i8o/platformio.ini
Normal file
39
examples/knx-h8i8o/platformio.ini
Normal file
@ -0,0 +1,39 @@
|
||||
;PlatformIO Project Configuration File
|
||||
;
|
||||
; Build options: build flags, source filter
|
||||
; Upload options: custom upload port, speed and extra flags
|
||||
; Library options: dependencies, extra library storages
|
||||
; Advanced options: extra scripting
|
||||
;
|
||||
; Please visit documentation for the other options and examples
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
[platformio]
|
||||
; We have to keep libdeps dir out the project directory otherwise,
|
||||
; library scanner seems to have issues so compilation fails
|
||||
libdeps_dir = /tmp/libdeps
|
||||
src_dir = .
|
||||
|
||||
;--- STM32/GD32 ---
|
||||
[env:h8i8o]
|
||||
platform = ststm32
|
||||
board = genericSTM32F103CB
|
||||
framework = arduino
|
||||
; We consider that the this projects is opened within its project directory
|
||||
; while working with VS Code.
|
||||
lib_extra_dirs = ../../../
|
||||
|
||||
lib_deps =
|
||||
knx
|
||||
|
||||
build_flags =
|
||||
-DENABLE_HWSERIAL1
|
||||
-DPIN_SERIAL1_TX=PA9
|
||||
-DPIN_SERIAL1_RX=PA10
|
||||
-DKNX_SERIAL=Serial1
|
||||
-DKNX_BUTTON=PA11
|
||||
-DKNX_LED=PA12
|
||||
-DMASK_VERSION=0x07B0
|
||||
-DKNX_NO_PRINT
|
||||
-Wno-unknown-pragmas
|
||||
|
||||
extra_scripts = ../scripts/stm32rdu.py
|
@ -20,7 +20,6 @@ board_build.usb_product="KNX RF - USB Interface"
|
||||
lib_deps =
|
||||
SPI
|
||||
Adafruit TinyUSB Library@0.7.1
|
||||
https://github.com/thelsing/FlashStorage.git
|
||||
knx
|
||||
|
||||
build_flags =
|
||||
|
34
examples/scripts/stm32rdu.py
Executable file
34
examples/scripts/stm32rdu.py
Executable file
@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env python
|
||||
from subprocess import run
|
||||
from datetime import datetime, timedelta
|
||||
from os.path import expanduser
|
||||
|
||||
ocddir = expanduser("~/.platformio/packages/tool-openocd/")
|
||||
chip = "stm32f1x"
|
||||
|
||||
def unlock(*args, **kwargs):
|
||||
print("Please connect the board within the next two minutes.")
|
||||
endtime = datetime.now() + timedelta(minutes = 1)
|
||||
ret = 1
|
||||
while ret != 0 and datetime.now() < endtime:
|
||||
ret = run(["bin/openocd", "-f", "interface/stlink.cfg", "-f", "target/" + chip + ".cfg", "-c", "init", "-c", "reset halt", "-c", chip + " unlock 0", "-c", "reset halt", "-c", "exit"], cwd = ocddir).returncode
|
||||
if ret != 0:
|
||||
print("Timeout")
|
||||
return ret
|
||||
|
||||
try:
|
||||
Import("env")
|
||||
except NameError:
|
||||
import sys
|
||||
if len(sys.argv) > 1:
|
||||
chip = sys.argv[1]
|
||||
if len(sys.argv) > 2:
|
||||
ocddir = sys.argv[2]
|
||||
unlock(None, None)
|
||||
else:
|
||||
ocddir = env.PioPlatform().get_package_dir("tool-openocd")
|
||||
options = env.GetProjectOptions()
|
||||
for option in options:
|
||||
if "unlock_chip" == option[0]:
|
||||
chip = option[1]
|
||||
env.AddCustomTarget("unlock", None, unlock)
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,10 @@
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
#ifndef KNX_DEBUG_SERIAL
|
||||
#define KNX_DEBUG_SERIAL Serial
|
||||
#endif
|
||||
|
||||
class ArduinoPlatform : public Platform
|
||||
{
|
||||
public:
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -84,6 +84,7 @@ int32_t AssociationTableObject::translateAsap(uint16_t asap)
|
||||
|
||||
void AssociationTableObject::beforeStateChange(LoadState& newState)
|
||||
{
|
||||
TableObject::beforeStateChange(newState);
|
||||
if (newState != LS_LOADED)
|
||||
return;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -21,7 +21,6 @@ BauSystemBCoupler::BauSystemBCoupler(Platform& platform) :
|
||||
#ifdef USE_DATASECURE
|
||||
_memory.addSaveRestore(&_secIfObj);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
ApplicationLayer& BauSystemBCoupler::applicationLayer()
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -18,14 +18,14 @@ template <class T> class CallbackProperty : public Property
|
||||
: Property(id, writeEnable, type, maxElements, access), _interfaceObject(io), _readCallback(readCallback)
|
||||
{}
|
||||
|
||||
virtual uint8_t read(uint16_t start, uint8_t count, uint8_t* data) const override
|
||||
uint8_t read(uint16_t start, uint8_t count, uint8_t* data) const override
|
||||
{
|
||||
if (count == 0 || _readCallback == nullptr || start > _maxElements || start + count > _maxElements + 1)
|
||||
return 0;
|
||||
|
||||
return _readCallback(_interfaceObject, start, count, data);
|
||||
}
|
||||
virtual uint8_t write(uint16_t start, uint8_t count, const uint8_t* data) override
|
||||
uint8_t write(uint16_t start, uint8_t count, const uint8_t* data) override
|
||||
{
|
||||
if (count == 0 || start > _maxElements || start + count > _maxElements + 1 || _writeCallback == nullptr)
|
||||
return 0;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
|
@ -14,17 +14,17 @@ template <class T> class FunctionProperty : public Property
|
||||
/* max_elements is set to 1, read and write level any value so we use Lv0, see 3.3.7 Application Layer p.68 */
|
||||
{}
|
||||
|
||||
virtual uint8_t read(uint16_t start, uint8_t count, uint8_t* data) const override
|
||||
uint8_t read(uint16_t start, uint8_t count, uint8_t* data) const override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual uint8_t write(uint16_t start, uint8_t count, const uint8_t* data) override
|
||||
uint8_t write(uint16_t start, uint8_t count, const uint8_t* data) override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual void command(uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) override
|
||||
void command(uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) override
|
||||
{
|
||||
if (length == 0 || _commandCallback == nullptr )
|
||||
{
|
||||
@ -34,7 +34,7 @@ template <class T> class FunctionProperty : public Property
|
||||
_commandCallback(_interfaceObject, data, length, resultData, resultLength);
|
||||
}
|
||||
|
||||
virtual void state(uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) override
|
||||
void state(uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) override
|
||||
{
|
||||
if (length == 0 || _stateCallback == nullptr )
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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;
|
||||
|
@ -29,9 +29,6 @@ enum KnxIpServiceType
|
||||
TunnelingAck = 0x421,
|
||||
RoutingIndication = 0x530,
|
||||
RoutingLostMessage = 0x531,
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
class KnxIpFrame
|
||||
|
@ -49,7 +49,6 @@ class KNXValue
|
||||
KNXValue& operator=(const float value);
|
||||
|
||||
private:
|
||||
|
||||
bool boolValue() const;
|
||||
uint8_t ucharValue() const;
|
||||
uint16_t ushortValue() const;
|
||||
|
@ -1,73 +1,115 @@
|
||||
#include "memory.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "bits.h"
|
||||
|
||||
Memory::Memory(Platform& platform, DeviceObject& deviceObject)
|
||||
: _platform(platform), _deviceObject(deviceObject)
|
||||
{
|
||||
}
|
||||
{}
|
||||
|
||||
Memory::~Memory()
|
||||
{}
|
||||
|
||||
void Memory::readMemory()
|
||||
{
|
||||
println("readMemory");
|
||||
if (_data != nullptr)
|
||||
|
||||
uint8_t* flashStart = _platform.getNonVolatileMemoryStart();
|
||||
size_t flashSize = _platform.getNonVolatileMemorySize();
|
||||
if (flashStart == nullptr)
|
||||
{
|
||||
println("no user flash available;");
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t flashSize = KNX_FLASH_SIZE;
|
||||
_data = _platform.getEepromBuffer(flashSize);
|
||||
|
||||
printHex("RESTORED ", _data, _metadataSize);
|
||||
printHex("RESTORED ", flashStart, _metadataSize);
|
||||
|
||||
uint16_t metadataBlockSize = alignToPageSize(_metadataSize);
|
||||
|
||||
_freeList = new MemoryBlock(_data + metadataBlockSize, flashSize - metadataBlockSize);
|
||||
_freeList = new MemoryBlock(flashStart + metadataBlockSize, flashSize - metadataBlockSize);
|
||||
|
||||
uint16_t apiVersion = 0;
|
||||
const uint8_t* buffer = popWord(apiVersion, flashStart);
|
||||
|
||||
uint16_t manufacturerId = 0;
|
||||
const uint8_t* buffer = popWord(manufacturerId, _data);
|
||||
buffer = popWord(manufacturerId, buffer);
|
||||
|
||||
uint8_t hardwareType[LEN_HARDWARE_TYPE] = {0};
|
||||
buffer = popByteArray(hardwareType, LEN_HARDWARE_TYPE, buffer);
|
||||
|
||||
uint16_t version = 0;
|
||||
buffer = popWord(version, buffer);
|
||||
|
||||
|
||||
|
||||
if (_deviceObject.manufacturerId() != manufacturerId
|
||||
|| _deviceObject.version() != version
|
||||
|| memcmp(_deviceObject.hardwareType(), hardwareType, LEN_HARDWARE_TYPE) != 0)
|
||||
|
||||
VersionCheckResult versionCheck = FlashAllInvalid;
|
||||
|
||||
// first check correct format of deviceObject-API
|
||||
if (_deviceObject.apiVersion == apiVersion)
|
||||
{
|
||||
println("saved memory doesn't match manufacturerId, version or hardwaretype");
|
||||
print("manufacturerId: ");
|
||||
print(manufacturerId, HEX);
|
||||
print(" ");
|
||||
println(_deviceObject.manufacturerId(), HEX);
|
||||
print("version: ");
|
||||
print(version, HEX);
|
||||
print(" ");
|
||||
println(_deviceObject.version(), HEX);
|
||||
print("hardwareType: ");
|
||||
printHex("", hardwareType, LEN_HARDWARE_TYPE);
|
||||
print(" ");
|
||||
printHex("", _deviceObject.hardwareType(), LEN_HARDWARE_TYPE);
|
||||
if (_versionCheckCallback != 0) {
|
||||
versionCheck = _versionCheckCallback(manufacturerId, hardwareType, version);
|
||||
// callback should provide infomation about version check failure reasons
|
||||
}
|
||||
else if (_deviceObject.manufacturerId() == manufacturerId &&
|
||||
memcmp(_deviceObject.hardwareType(), hardwareType, LEN_HARDWARE_TYPE) == 0)
|
||||
{
|
||||
if (_deviceObject.version() == version) {
|
||||
versionCheck = FlashValid;
|
||||
}
|
||||
else
|
||||
{
|
||||
versionCheck = FlashTablesInvalid;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
println("manufacturerId or hardwareType are different");
|
||||
print("expexted manufacturerId: ");
|
||||
print(_deviceObject.manufacturerId(), HEX);
|
||||
print(", stored manufacturerId: ");
|
||||
println(manufacturerId, HEX);
|
||||
print("expexted hardwareType: ");
|
||||
printHex("", _deviceObject.hardwareType(), LEN_HARDWARE_TYPE);
|
||||
print(", stored hardwareType: ");
|
||||
printHex("", hardwareType, LEN_HARDWARE_TYPE);
|
||||
println("");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
println("DataObject api changed, any data stored in flash is invalid.");
|
||||
print("expexted DataObject api version: ");
|
||||
print(_deviceObject.apiVersion, HEX);
|
||||
print(", stored api version: ");
|
||||
println(apiVersion, HEX);
|
||||
}
|
||||
|
||||
if (versionCheck == FlashAllInvalid)
|
||||
{
|
||||
println("ETS has to reprogram PA and application!");
|
||||
return;
|
||||
}
|
||||
|
||||
println("manufacturerId, version and hardwareType matches");
|
||||
println("restoring data from flash...");
|
||||
print("saverestores ");
|
||||
println(_saveCount);
|
||||
for (int i = 0; i < _saveCount; i++)
|
||||
{
|
||||
println(_data - buffer);
|
||||
println(flashStart - buffer);
|
||||
println(".");
|
||||
buffer = _saveRestores[i]->restore(buffer);
|
||||
}
|
||||
println("restored saveRestores");
|
||||
if (versionCheck == FlashTablesInvalid)
|
||||
{
|
||||
println("TableObjects are referring to an older firmware version and are not loaded");
|
||||
return;
|
||||
}
|
||||
print("tableObjs ");
|
||||
println(_tableObjCount);
|
||||
for (int i = 0; i < _tableObjCount; i++)
|
||||
{
|
||||
println(_data - buffer);
|
||||
println(flashStart - buffer);
|
||||
println(".");
|
||||
buffer = _tableObjects[i]->restore(buffer);
|
||||
uint16_t memorySize = 0;
|
||||
@ -84,48 +126,62 @@ void Memory::readMemory()
|
||||
|
||||
void Memory::writeMemory()
|
||||
{
|
||||
uint8_t* buffer = _data;
|
||||
buffer = pushWord(_deviceObject.manufacturerId(), buffer);
|
||||
buffer = pushByteArray(_deviceObject.hardwareType(), LEN_HARDWARE_TYPE, buffer);
|
||||
buffer = pushWord(_deviceObject.version(), buffer);
|
||||
// first get the necessary size of the writeBuffer
|
||||
size_t writeBufferSize = _metadataSize;
|
||||
for (int i = 0; i < _saveCount; i++)
|
||||
writeBufferSize = MAX(writeBufferSize, _saveRestores[i]->saveSize());
|
||||
|
||||
for (int i = 0; i < _tableObjCount; i++)
|
||||
writeBufferSize = MAX(writeBufferSize, _tableObjects[i]->saveSize() + 2 /*for memory pos*/);
|
||||
|
||||
uint8_t buffer[writeBufferSize];
|
||||
uint32_t flashPos = 0;
|
||||
uint8_t* bufferPos = buffer;
|
||||
|
||||
bufferPos = pushWord(_deviceObject.apiVersion, bufferPos);
|
||||
bufferPos = pushWord(_deviceObject.manufacturerId(), bufferPos);
|
||||
bufferPos = pushByteArray(_deviceObject.hardwareType(), LEN_HARDWARE_TYPE, bufferPos);
|
||||
bufferPos = pushWord(_deviceObject.version(), bufferPos);
|
||||
|
||||
flashPos = _platform.writeNonVolatileMemory(flashPos, buffer, bufferPos - buffer);
|
||||
|
||||
print("save saveRestores ");
|
||||
println(_saveCount);
|
||||
for (int i = 0; i < _saveCount; i++)
|
||||
{
|
||||
println(_data - buffer);
|
||||
println(".");
|
||||
//println((long)_saveRestores[i], HEX);
|
||||
buffer = _saveRestores[i]->save(buffer);
|
||||
bufferPos = _saveRestores[i]->save(buffer);
|
||||
flashPos = _platform.writeNonVolatileMemory(flashPos, buffer, bufferPos - buffer);
|
||||
}
|
||||
|
||||
print("save tableobjs ");
|
||||
println(_tableObjCount);
|
||||
for (int i = 0; i < _tableObjCount; i++)
|
||||
{
|
||||
println(_data - buffer);
|
||||
println(".");
|
||||
//println((long)_tableObjects[i], HEX);
|
||||
buffer = _tableObjects[i]->save(buffer);
|
||||
bufferPos = _tableObjects[i]->save(buffer);
|
||||
|
||||
//save to size of the memoryblock for tableobject too, so that we can rebuild the usedList and freeList
|
||||
if (_tableObjects[i]->_data != nullptr)
|
||||
{
|
||||
|
||||
MemoryBlock* block = findBlockInList(_usedList, _tableObjects[i]->_data);
|
||||
if (block == nullptr)
|
||||
{
|
||||
println("_data of TableObject not in errorlist");
|
||||
println("_data of TableObject not in _usedList");
|
||||
_platform.fatalError();
|
||||
}
|
||||
buffer = pushWord(block->size, buffer);
|
||||
bufferPos = pushWord(block->size, bufferPos);
|
||||
}
|
||||
else
|
||||
buffer = pushWord(0, buffer);
|
||||
bufferPos = pushWord(0, bufferPos);
|
||||
|
||||
flashPos = _platform.writeNonVolatileMemory(flashPos, buffer, bufferPos - buffer);
|
||||
}
|
||||
|
||||
_platform.commitToEeprom();
|
||||
printHex("SAVED ", _data, _metadataSize);
|
||||
_platform.commitNonVolatileMemory();
|
||||
}
|
||||
|
||||
void Memory::saveMemory()
|
||||
{
|
||||
_platform.commitNonVolatileMemory();
|
||||
}
|
||||
|
||||
void Memory::addSaveRestore(SaveRestore* obj)
|
||||
@ -151,7 +207,7 @@ void Memory::addSaveRestore(TableObject* obj)
|
||||
|
||||
uint8_t* Memory::allocMemory(size_t size)
|
||||
{
|
||||
// always allocate aligned to 32 bit
|
||||
// always allocate aligned to pagesize
|
||||
size = alignToPageSize(size);
|
||||
|
||||
MemoryBlock* freeBlock = _freeList;
|
||||
@ -220,19 +276,19 @@ void Memory::freeMemory(uint8_t* ptr)
|
||||
|
||||
void Memory::writeMemory(uint32_t relativeAddress, size_t size, uint8_t* data)
|
||||
{
|
||||
memcpy(toAbsolute(relativeAddress), data, size);
|
||||
_platform.writeNonVolatileMemory(relativeAddress, data, size);
|
||||
}
|
||||
|
||||
|
||||
uint8_t* Memory::toAbsolute(uint32_t relativeAddress)
|
||||
{
|
||||
return _data + (ptrdiff_t)relativeAddress;
|
||||
return _platform.getNonVolatileMemoryStart() + (ptrdiff_t)relativeAddress;
|
||||
}
|
||||
|
||||
|
||||
uint32_t Memory::toRelative(uint8_t* absoluteAddress)
|
||||
{
|
||||
return absoluteAddress - _data;
|
||||
return absoluteAddress - _platform.getNonVolatileMemoryStart();
|
||||
}
|
||||
|
||||
MemoryBlock* Memory::removeFromList(MemoryBlock* head, MemoryBlock* item)
|
||||
@ -354,8 +410,9 @@ void Memory::addToFreeList(MemoryBlock* block)
|
||||
|
||||
uint16_t Memory::alignToPageSize(size_t size)
|
||||
{
|
||||
// to 32 bit for now
|
||||
return (size + 3) & ~0x3;
|
||||
size_t pageSize = 4; //_platform.flashPageSize(); // align to 32bit for now, as aligning to flash-page-size causes side effects in programming
|
||||
// pagesize should be a multiply of two
|
||||
return (size + pageSize - 1) & (-1*pageSize);
|
||||
}
|
||||
|
||||
MemoryBlock* Memory::findBlockInList(MemoryBlock* head, uint8_t* address)
|
||||
@ -429,3 +486,13 @@ void Memory::addNewUsedBlock(uint8_t* address, size_t size)
|
||||
MemoryBlock* newUsedBlock = new MemoryBlock(address, size);
|
||||
addToUsedList(newUsedBlock);
|
||||
}
|
||||
|
||||
void Memory::versionCheckCallback(VersionCheckCallback func)
|
||||
{
|
||||
_versionCheckCallback = func;
|
||||
}
|
||||
|
||||
VersionCheckCallback Memory::versionCheckCallback()
|
||||
{
|
||||
return _versionCheckCallback;
|
||||
}
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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,
|
||||
|
@ -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];
|
||||
|
@ -1,12 +1,15 @@
|
||||
#include "platform.h"
|
||||
|
||||
#include "bits.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
|
||||
NvMemoryType Platform::NonVolatileMemoryType()
|
||||
{
|
||||
return _memoryType;
|
||||
}
|
||||
|
||||
|
||||
void Platform::NonVolatileMemoryType(NvMemoryType type)
|
||||
{
|
||||
_memoryType = type;
|
||||
@ -97,3 +100,155 @@ int Platform::readBytesMultiCast(uint8_t *buffer, uint16_t maxLen)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t Platform::flashEraseBlockSize()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t Platform::flashPageSize()
|
||||
{
|
||||
// align to 32bit as default for Eeprom Emulation plattforms
|
||||
return 4;
|
||||
}
|
||||
|
||||
uint8_t *Platform::userFlashStart()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t Platform::userFlashSizeEraseBlocks()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Platform::flashErase(uint16_t eraseBlockNum)
|
||||
{}
|
||||
|
||||
void Platform::flashWritePage(uint16_t pageNumber, uint8_t* data)
|
||||
{}
|
||||
|
||||
uint8_t * Platform::getEepromBuffer(uint16_t size)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Platform::commitToEeprom()
|
||||
{}
|
||||
|
||||
uint8_t* Platform::getNonVolatileMemoryStart()
|
||||
{
|
||||
if(_memoryType == Flash)
|
||||
return userFlashStart();
|
||||
else
|
||||
return getEepromBuffer(KNX_FLASH_SIZE);
|
||||
}
|
||||
|
||||
size_t Platform::getNonVolatileMemorySize()
|
||||
{
|
||||
if(_memoryType == Flash)
|
||||
return userFlashSizeEraseBlocks() * flashEraseBlockSize() * flashPageSize();
|
||||
else
|
||||
return KNX_FLASH_SIZE;
|
||||
}
|
||||
|
||||
void Platform::commitNonVolatileMemory()
|
||||
{
|
||||
if(_memoryType == Flash)
|
||||
{
|
||||
if(_bufferedEraseblockNumber > -1 && _bufferedEraseblockDirty)
|
||||
{
|
||||
writeBufferedEraseBlock();
|
||||
|
||||
free(_eraseblockBuffer);
|
||||
_eraseblockBuffer = nullptr;
|
||||
_bufferedEraseblockNumber = -1; // does that make sense?
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
commitToEeprom();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t Platform::writeNonVolatileMemory(uint32_t relativeAddress, uint8_t* buffer, size_t size)
|
||||
{
|
||||
if(_memoryType == Flash)
|
||||
{
|
||||
while (size > 0)
|
||||
{
|
||||
loadEraseblockContaining(relativeAddress);
|
||||
uint32_t start = _bufferedEraseblockNumber * (flashEraseBlockSize() * flashPageSize());
|
||||
uint32_t end = start + (flashEraseBlockSize() * flashPageSize());
|
||||
|
||||
ptrdiff_t offset = relativeAddress - start;
|
||||
ptrdiff_t length = end - relativeAddress;
|
||||
if(length > size)
|
||||
length = size;
|
||||
memcpy(_eraseblockBuffer + offset, buffer, length);
|
||||
_bufferedEraseblockDirty = true;
|
||||
|
||||
relativeAddress += length;
|
||||
buffer += length;
|
||||
size -= length;
|
||||
}
|
||||
return relativeAddress;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(getEepromBuffer(KNX_FLASH_SIZE)+relativeAddress, buffer, size);
|
||||
return relativeAddress+size;
|
||||
}
|
||||
}
|
||||
|
||||
void Platform::loadEraseblockContaining(uint32_t relativeAddress)
|
||||
{
|
||||
int32_t blockNum = getEraseBlockNumberOf(relativeAddress);
|
||||
if (blockNum < 0)
|
||||
{
|
||||
println("loadEraseblockContaining could not get valid eraseblock number");
|
||||
fatalError();
|
||||
}
|
||||
|
||||
if (blockNum != _bufferedEraseblockNumber && _bufferedEraseblockNumber >= 0)
|
||||
writeBufferedEraseBlock();
|
||||
|
||||
bufferEraseBlock(blockNum);
|
||||
}
|
||||
|
||||
int32_t Platform::getEraseBlockNumberOf(uint32_t relativeAddress)
|
||||
{
|
||||
return relativeAddress / (flashEraseBlockSize() * flashPageSize());
|
||||
}
|
||||
|
||||
|
||||
void Platform::writeBufferedEraseBlock()
|
||||
{
|
||||
if(_bufferedEraseblockNumber > -1 && _bufferedEraseblockDirty)
|
||||
{
|
||||
flashErase(_bufferedEraseblockNumber);
|
||||
for(int i = 0; i < flashEraseBlockSize(); i++)
|
||||
{
|
||||
int32_t pageNumber = _bufferedEraseblockNumber * flashEraseBlockSize() + i;
|
||||
uint8_t *data = _eraseblockBuffer + flashPageSize() * i;
|
||||
flashWritePage(pageNumber, data);
|
||||
}
|
||||
_bufferedEraseblockDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Platform::bufferEraseBlock(uint32_t eraseBlockNumber)
|
||||
{
|
||||
if(_bufferedEraseblockNumber == eraseBlockNumber)
|
||||
return;
|
||||
|
||||
if(_eraseblockBuffer == nullptr)
|
||||
{
|
||||
_eraseblockBuffer = (uint8_t*)malloc(flashEraseBlockSize() * flashPageSize());
|
||||
}
|
||||
memcpy(_eraseblockBuffer, userFlashStart() + eraseBlockNumber * flashEraseBlockSize() * flashPageSize(), flashEraseBlockSize() * flashPageSize());
|
||||
|
||||
_bufferedEraseblockNumber = eraseBlockNumber;
|
||||
_bufferedEraseblockDirty = false;
|
||||
}
|
||||
|
@ -4,6 +4,11 @@
|
||||
#include <stddef.h>
|
||||
#include "save_restore.h"
|
||||
|
||||
#ifndef KNX_FLASH_SIZE
|
||||
#define KNX_FLASH_SIZE 1024
|
||||
#pragma warning "KNX_FLASH_SIZE not defined, using 1024"
|
||||
#endif
|
||||
|
||||
enum NvMemoryType
|
||||
{
|
||||
Eeprom,
|
||||
@ -49,21 +54,62 @@ class Platform
|
||||
virtual void setupSpi();
|
||||
virtual void closeSpi();
|
||||
virtual int readWriteSpi(uint8_t *data, size_t len);
|
||||
#if 0
|
||||
// Flash memory
|
||||
virtual size_t flashEraseBlockSize(); // in pages
|
||||
virtual size_t flashPageSize(); // in bytes
|
||||
virtual uint8_t* userFlashStart(); // start of user flash aligned to start of an erase block
|
||||
virtual size_t userFlashSizeEraseBlocks(); // in eraseBlocks
|
||||
virtual void flashErase(uint16_t eraseBlockNum); //relativ to userFlashStart
|
||||
virtual void flashWritePage(uint16_t pageNumber, uint8_t* data); //write a single page to flash (pageNumber relative to userFashStart
|
||||
#endif
|
||||
virtual uint8_t* getEepromBuffer(uint16_t size) = 0;
|
||||
virtual void commitToEeprom() = 0;
|
||||
|
||||
//Memory
|
||||
|
||||
// --- Overwrite these methods in the device-plattform to use the EEPROM Emulation API for UserMemory ----
|
||||
//
|
||||
// --- changes to the UserMemory are written directly into the address space starting at getEepromBuffer
|
||||
// --- commitToEeprom must save this to a non-volatile area if neccessary
|
||||
virtual uint8_t* getEepromBuffer(uint16_t size);
|
||||
virtual void commitToEeprom();
|
||||
// -------------------------------------------------------------------------------------------------------
|
||||
|
||||
virtual uint8_t* getNonVolatileMemoryStart();
|
||||
virtual size_t getNonVolatileMemorySize();
|
||||
virtual void commitNonVolatileMemory();
|
||||
// address is relative to start of nonvolatile memory
|
||||
virtual uint32_t writeNonVolatileMemory(uint32_t relativeAddress, uint8_t* buffer, size_t size);
|
||||
|
||||
NvMemoryType NonVolatileMemoryType();
|
||||
void NonVolatileMemoryType(NvMemoryType type);
|
||||
|
||||
// --- Overwrite these methods in the device-plattform to use flash memory handling by the knx stack ---
|
||||
// --- also set _memoryType = Flash in the device-plattform's contructor
|
||||
// --- optional: overwrite writeBufferedEraseBlock() in the device-plattform to reduce overhead when flashing multiple pages
|
||||
|
||||
// size of one flash page in bytes
|
||||
virtual size_t flashPageSize();
|
||||
|
||||
protected:
|
||||
// size of one EraseBlock in pages
|
||||
virtual size_t flashEraseBlockSize();
|
||||
// start of user flash aligned to start of an erase block
|
||||
virtual uint8_t* userFlashStart();
|
||||
// size of the user flash in EraseBlocks
|
||||
virtual size_t userFlashSizeEraseBlocks();
|
||||
//relativ to userFlashStart
|
||||
virtual void flashErase(uint16_t eraseBlockNum);
|
||||
//write a single page to flash (pageNumber relative to userFashStart
|
||||
virtual void flashWritePage(uint16_t pageNumber, uint8_t* data);
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
NvMemoryType _memoryType = Eeprom;
|
||||
|
||||
void loadEraseblockContaining(uint32_t relativeAddress);
|
||||
int32_t getEraseBlockNumberOf(uint32_t relativeAddress);
|
||||
// writes _eraseblockBuffer to flash
|
||||
virtual void writeBufferedEraseBlock();
|
||||
// copies a EraseBlock into the _eraseblockBuffer
|
||||
void bufferEraseBlock(uint32_t eraseBlockNumber);
|
||||
|
||||
// in theory we would have to use this buffer for memory reads too,
|
||||
// but because ets always restarts the device after programming it
|
||||
// we can ignore this issue
|
||||
uint8_t* _eraseblockBuffer = nullptr;
|
||||
int32_t _bufferedEraseblockNumber = -1;
|
||||
bool _bufferedEraseblockDirty = false;
|
||||
};
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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*);
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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];
|
||||
|
@ -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();
|
||||
|
||||
|
@ -343,7 +343,6 @@ void TransportLayer::dataIndividualConfirm(AckType ack, uint16_t destination, Ho
|
||||
A5(destination);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
case Disconnect:
|
||||
|
@ -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)
|
||||
|
@ -15,35 +15,46 @@
|
||||
#define ICACHE_RAM_ATTR
|
||||
#endif
|
||||
|
||||
ICACHE_RAM_ATTR void buttonUp()
|
||||
#ifndef PROG_BTN_PRESS_MIN_MILLIS
|
||||
#define PROG_BTN_PRESS_MIN_MILLIS 50
|
||||
#endif
|
||||
|
||||
#ifndef PROG_BTN_PRESS_MAX_MILLIS
|
||||
#define PROG_BTN_PRESS_MAX_MILLIS 500
|
||||
#endif
|
||||
|
||||
|
||||
ICACHE_RAM_ATTR void buttonEvent()
|
||||
{
|
||||
static uint32_t lastpressed=0;
|
||||
if (millis() - lastpressed > 200){
|
||||
static uint32_t lastEvent=0;
|
||||
uint32_t diff = millis() - lastEvent;
|
||||
if (diff >= PROG_BTN_PRESS_MIN_MILLIS && diff <= PROG_BTN_PRESS_MAX_MILLIS){
|
||||
knx.toggleProgMode();
|
||||
lastpressed = millis();
|
||||
}
|
||||
|
||||
lastEvent = millis();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ARDUINO_ARCH_SAMD
|
||||
// predefined global instance for TP or RF or TP/RF coupler
|
||||
#if MASK_VERSION == 0x07B0
|
||||
KnxFacade<SamdPlatform, Bau07B0> knx(buttonUp);
|
||||
KnxFacade<SamdPlatform, Bau07B0> knx(buttonEvent);
|
||||
#elif MASK_VERSION == 0x27B0
|
||||
KnxFacade<SamdPlatform, Bau27B0> knx(buttonUp);
|
||||
KnxFacade<SamdPlatform, Bau27B0> knx(buttonEvent);
|
||||
#elif MASK_VERSION == 0x2920
|
||||
KnxFacade<SamdPlatform, Bau2920> knx(buttonUp);
|
||||
KnxFacade<SamdPlatform, Bau2920> knx(buttonEvent);
|
||||
#else
|
||||
#error "Mask version not supported on ARDUINO_ARCH_SAMD"
|
||||
#endif
|
||||
#elif defined(ARDUINO_ARCH_RP2040)
|
||||
// predefined global instance for TP or RF or TP/RF coupler
|
||||
#if MASK_VERSION == 0x07B0
|
||||
KnxFacade<RP2040ArduinoPlatform, Bau07B0> knx(buttonUp);
|
||||
KnxFacade<RP2040ArduinoPlatform, Bau07B0> knx(buttonEvent);
|
||||
#elif MASK_VERSION == 0x27B0
|
||||
KnxFacade<RP2040ArduinoPlatform, Bau27B0> knx(buttonUp);
|
||||
KnxFacade<RP2040ArduinoPlatform, Bau27B0> knx(buttonEvent);
|
||||
#elif MASK_VERSION == 0x2920
|
||||
KnxFacade<RP2040ArduinoPlatform, Bau2920> knx(buttonUp);
|
||||
KnxFacade<RP2040ArduinoPlatform, Bau2920> knx(buttonEvent);
|
||||
#else
|
||||
#error "Mask version not supported on ARDUINO_ARCH_RP2040"
|
||||
#endif
|
||||
@ -51,11 +62,11 @@
|
||||
#elif defined(ARDUINO_ARCH_ESP8266)
|
||||
// predefined global instance for TP or IP or TP/IP coupler
|
||||
#if MASK_VERSION == 0x07B0
|
||||
KnxFacade<EspPlatform, Bau07B0> knx(buttonUp);
|
||||
KnxFacade<EspPlatform, Bau07B0> knx(buttonEvent);
|
||||
#elif MASK_VERSION == 0x57B0
|
||||
KnxFacade<EspPlatform, Bau57B0> knx(buttonUp);
|
||||
KnxFacade<EspPlatform, Bau57B0> knx(buttonEvent);
|
||||
#elif MASK_VERSION == 0x091A
|
||||
KnxFacade<EspPlatform, Bau091A> knx(buttonUp);
|
||||
KnxFacade<EspPlatform, Bau091A> knx(buttonEvent);
|
||||
#else
|
||||
#error "Mask version not supported on ARDUINO_ARCH_ESP8266"
|
||||
#endif
|
||||
@ -63,18 +74,18 @@
|
||||
#elif defined(ARDUINO_ARCH_ESP32)
|
||||
// predefined global instance for TP or IP or TP/IP coupler
|
||||
#if MASK_VERSION == 0x07B0
|
||||
KnxFacade<Esp32Platform, Bau07B0> knx(buttonUp);
|
||||
KnxFacade<Esp32Platform, Bau07B0> knx(buttonEvent);
|
||||
#elif MASK_VERSION == 0x57B0
|
||||
KnxFacade<Esp32Platform, Bau57B0> knx(buttonUp);
|
||||
KnxFacade<Esp32Platform, Bau57B0> knx(buttonEvent);
|
||||
#elif MASK_VERSION == 0x091A
|
||||
KnxFacade<Esp32Platform, Bau091A> knx(buttonUp);
|
||||
KnxFacade<Esp32Platform, Bau091A> knx(buttonEvent);
|
||||
#else
|
||||
#error "Mask version not supported on ARDUINO_ARCH_ESP32"
|
||||
#endif
|
||||
|
||||
#elif defined(ARDUINO_ARCH_STM32)
|
||||
#if MASK_VERSION == 0x07B0
|
||||
KnxFacade<Stm32Platform, Bau07B0> knx(buttonUp);
|
||||
KnxFacade<Stm32Platform, Bau07B0> knx(buttonEvent);
|
||||
#else
|
||||
#error "Mask version not supported on ARDUINO_ARCH_STM32"
|
||||
#endif
|
||||
|
@ -48,13 +48,20 @@
|
||||
#else
|
||||
#if !defined(LED_BUILTIN)
|
||||
#define LED_BUILTIN 5 // see GPIO_PinConfig gpioPinConfigs[]
|
||||
#endif
|
||||
#endif
|
||||
#include "cc1310_platform.h"
|
||||
#ifndef KNX_NO_AUTOMATIC_GLOBAL_INSTANCE
|
||||
extern void buttonUp();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef KNX_LED
|
||||
#define KNX_LED LED_BUILTIN
|
||||
#endif
|
||||
#ifndef KNX_BUTTON
|
||||
#define KNX_BUTTON 0
|
||||
#endif
|
||||
|
||||
typedef const uint8_t* (*RestoreCallback)(const uint8_t* buffer);
|
||||
typedef uint8_t* (*SaveCallback)(uint8_t* buffer);
|
||||
typedef void (*IsrFunctionPtr)();
|
||||
@ -73,6 +80,7 @@ template <class P, class B> class KnxFacade : private SaveRestore
|
||||
|
||||
KnxFacade(B& bau) : _bau(bau)
|
||||
{
|
||||
_platformPtr = static_cast<P*>(&bau.platform());
|
||||
manufacturerId(0xfa);
|
||||
bauNumber(platform().uniqueSerialNumber());
|
||||
_bau.addSaveRestore(this);
|
||||
@ -176,24 +184,7 @@ template <class P, class B> class KnxFacade : private SaveRestore
|
||||
_progLedOnCallback = progLedOnCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns RISING if interrupt is created in a rising signal, FALLING otherwise
|
||||
*/
|
||||
uint32_t buttonPinInterruptOn()
|
||||
{
|
||||
return _buttonPinInterruptOn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if the programming button creates a RISING or a FALLING signal.
|
||||
*
|
||||
* Set to RISING for GPIO--BUTTON--VDD or to FALLING for GPIO--BUTTON--GND
|
||||
*/
|
||||
void buttonPinInterruptOn(uint32_t value)
|
||||
{
|
||||
_buttonPinInterruptOn = value;
|
||||
}
|
||||
|
||||
|
||||
uint32_t buttonPin()
|
||||
{
|
||||
return _buttonPin;
|
||||
@ -280,9 +271,9 @@ template <class P, class B> class KnxFacade : private SaveRestore
|
||||
{
|
||||
// Workaround for https://github.com/arduino/ArduinoCore-samd/issues/587
|
||||
#if (ARDUINO_API_VERSION >= 10200)
|
||||
attachInterrupt(_buttonPin, _progButtonISRFuncPtr, (PinStatus)_buttonPinInterruptOn);
|
||||
attachInterrupt(_buttonPin, _progButtonISRFuncPtr, (PinStatus)CHANGE);
|
||||
#else
|
||||
attachInterrupt(_buttonPin, _progButtonISRFuncPtr, _buttonPinInterruptOn);
|
||||
attachInterrupt(_buttonPin, _progButtonISRFuncPtr, CHANGE);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -400,7 +391,18 @@ template <class P, class B> class KnxFacade : private SaveRestore
|
||||
|
||||
void restart(uint16_t individualAddress)
|
||||
{
|
||||
_bau.restartRequest(individualAddress);
|
||||
SecurityControl sc = {false, None};
|
||||
_bau.restartRequest(individualAddress, sc);
|
||||
}
|
||||
|
||||
void beforeRestartCallback(BeforeRestartCallback func)
|
||||
{
|
||||
_bau.beforeRestartCallback(func);
|
||||
}
|
||||
|
||||
BeforeRestartCallback beforeRestartCallback()
|
||||
{
|
||||
return _bau.beforeRestartCallback();
|
||||
}
|
||||
|
||||
private:
|
||||
@ -410,9 +412,8 @@ template <class P, class B> class KnxFacade : private SaveRestore
|
||||
ProgLedOnCallback _progLedOnCallback = 0;
|
||||
ProgLedOffCallback _progLedOffCallback = 0;
|
||||
uint32_t _ledPinActiveOn = LOW;
|
||||
uint32_t _ledPin = LED_BUILTIN;
|
||||
uint32_t _buttonPinInterruptOn = RISING;
|
||||
uint32_t _buttonPin = 0;
|
||||
uint32_t _ledPin = KNX_LED;
|
||||
uint32_t _buttonPin = KNX_BUTTON;
|
||||
SaveCallback _saveCallback = 0;
|
||||
RestoreCallback _restoreCallback = 0;
|
||||
volatile bool _toggleProgMode = false;
|
||||
|
@ -1,16 +1,22 @@
|
||||
/*-----------------------------------------------------
|
||||
|
||||
Plattform for Raspberry Pi Pico and other RP2040 boards
|
||||
by SirSydom <com@sirsydom.de> 2021-2022
|
||||
|
||||
made to work with arduino-pico - "Raspberry Pi Pico Arduino core, for all RP2040 boards"
|
||||
by Earl E. Philhower III https://github.com/earlephilhower/arduino-pico V1.11.0
|
||||
|
||||
by SirSydom <com@sirsydom.de> 2021-2022
|
||||
|
||||
RTTI must be set to enabled in the board options
|
||||
|
||||
A maximum of 4kB emulated EEPROM is supported.
|
||||
For more, use or own emulation (maybe with littlefs)
|
||||
Uses direct flash reading/writing.
|
||||
Size ist defined by KNX_FLASH_SIZE (default 4k) - must be a multiple of 4096.
|
||||
Offset in Flash is defined by KNX_FLASH_OFFSET (default 1,5MiB / 0x180000) - must be a multiple of 4096.
|
||||
|
||||
EEPROM Emulation from arduino-pico core (max 4k) can be use by defining USE_RP2040_EEPROM_EMULATION
|
||||
|
||||
A RAM-buffered Flash can be use by defining USE_RP2040_LARGE_EEPROM_EMULATION
|
||||
|
||||
|
||||
----------------------------------------------------*/
|
||||
|
||||
@ -25,17 +31,37 @@ For more, use or own emulation (maybe with littlefs)
|
||||
#include <EEPROM.h> // EEPROM emulation in flash, part of Earl E Philhowers Pi Pico Arduino support
|
||||
#include <pico/unique_id.h> // from Pico SDK
|
||||
#include <hardware/watchdog.h> // from Pico SDK
|
||||
#include <hardware/flash.h> // from Pico SDK
|
||||
|
||||
#define FLASHPTR ((uint8_t*)XIP_BASE + KNX_FLASH_OFFSET)
|
||||
|
||||
#if KNX_FLASH_SIZE%4096
|
||||
#error "KNX_FLASH_SIZE must be multiple of 4096"
|
||||
#endif
|
||||
|
||||
#if KNX_FLASH_OFFSET%4096
|
||||
#error "KNX_FLASH_OFFSET must be multiple of 4096"
|
||||
#endif
|
||||
|
||||
#ifndef KNX_SERIAL
|
||||
#define KNX_SERIAL Serial1
|
||||
#endif
|
||||
|
||||
RP2040ArduinoPlatform::RP2040ArduinoPlatform()
|
||||
#ifndef KNX_NO_DEFAULT_UART
|
||||
: ArduinoPlatform(&Serial1)
|
||||
: ArduinoPlatform(&KNX_SERIAL)
|
||||
#endif
|
||||
{
|
||||
#ifndef USE_RP2040_EEPROM_EMULATION
|
||||
_memoryType = Flash;
|
||||
#endif
|
||||
}
|
||||
|
||||
RP2040ArduinoPlatform::RP2040ArduinoPlatform( HardwareSerial* s) : ArduinoPlatform(s)
|
||||
{
|
||||
#ifndef USE_RP2040_EEPROM_EMULATION
|
||||
_memoryType = Flash;
|
||||
#endif
|
||||
}
|
||||
|
||||
void RP2040ArduinoPlatform::setupUart()
|
||||
@ -54,7 +80,14 @@ void RP2040ArduinoPlatform::setupUart()
|
||||
uint32_t RP2040ArduinoPlatform::uniqueSerialNumber()
|
||||
{
|
||||
pico_unique_board_id_t id; // 64Bit unique serial number from the QSPI flash
|
||||
pico_get_unique_board_id(&id);
|
||||
|
||||
noInterrupts();
|
||||
rp2040.idleOtherCore();
|
||||
|
||||
flash_get_unique_id(id.id); //pico_get_unique_board_id(&id);
|
||||
|
||||
rp2040.resumeOtherCore();
|
||||
interrupts();
|
||||
|
||||
// use lower 4 byte and convert to unit32_t
|
||||
uint32_t uid = ((uint32_t)(id.id[4]) << 24) | ((uint32_t)(id.id[5]) << 16) | ((uint32_t)(id.id[6]) << 8) | (uint32_t)(id.id[7]);
|
||||
@ -68,6 +101,47 @@ void RP2040ArduinoPlatform::restart()
|
||||
watchdog_reboot(0,0,0);
|
||||
}
|
||||
|
||||
#ifdef USE_RP2040_EEPROM_EMULATION
|
||||
|
||||
#pragma warning "Using EEPROM Simulation"
|
||||
|
||||
#ifdef USE_RP2040_LARGE_EEPROM_EMULATION
|
||||
|
||||
uint8_t * RP2040ArduinoPlatform::getEepromBuffer(uint16_t size)
|
||||
{
|
||||
if(size%4096)
|
||||
{
|
||||
println("KNX_FLASH_SIZE must be a multiple of 4096");
|
||||
fatalError();
|
||||
}
|
||||
|
||||
if(!_rambuff_initialized)
|
||||
{
|
||||
memcpy(_rambuff, FLASHPTR, KNX_FLASH_SIZE);
|
||||
_rambuff_initialized = true;
|
||||
}
|
||||
|
||||
return _rambuff;
|
||||
}
|
||||
|
||||
void RP2040ArduinoPlatform::commitToEeprom()
|
||||
{
|
||||
noInterrupts();
|
||||
rp2040.idleOtherCore();
|
||||
|
||||
//ToDo: write block-by-block to prevent writing of untouched blocks
|
||||
if(memcmp(_rambuff, FLASHPTR, KNX_FLASH_SIZE))
|
||||
{
|
||||
flash_range_erase (KNX_FLASH_OFFSET, KNX_FLASH_SIZE);
|
||||
flash_range_program(KNX_FLASH_OFFSET, _rambuff, KNX_FLASH_SIZE);
|
||||
}
|
||||
|
||||
rp2040.resumeOtherCore();
|
||||
interrupts();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
uint8_t * RP2040ArduinoPlatform::getEepromBuffer(uint16_t size)
|
||||
{
|
||||
if(size > 4096)
|
||||
@ -91,6 +165,73 @@ void RP2040ArduinoPlatform::commitToEeprom()
|
||||
{
|
||||
EEPROM.commit();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
size_t RP2040ArduinoPlatform::flashEraseBlockSize()
|
||||
{
|
||||
return 16; // 16 pages x 256byte/page = 4096byte
|
||||
}
|
||||
|
||||
size_t RP2040ArduinoPlatform::flashPageSize()
|
||||
{
|
||||
return 256;
|
||||
}
|
||||
|
||||
uint8_t* RP2040ArduinoPlatform::userFlashStart()
|
||||
{
|
||||
return (uint8_t*)XIP_BASE + KNX_FLASH_OFFSET;
|
||||
}
|
||||
|
||||
size_t RP2040ArduinoPlatform::userFlashSizeEraseBlocks()
|
||||
{
|
||||
if(KNX_FLASH_SIZE <= 0)
|
||||
return 0;
|
||||
else
|
||||
return ( (KNX_FLASH_SIZE - 1) / (flashPageSize() * flashEraseBlockSize())) + 1;
|
||||
}
|
||||
|
||||
void RP2040ArduinoPlatform::flashErase(uint16_t eraseBlockNum)
|
||||
{
|
||||
noInterrupts();
|
||||
rp2040.idleOtherCore();
|
||||
|
||||
flash_range_erase (KNX_FLASH_OFFSET + eraseBlockNum * flashPageSize() * flashEraseBlockSize(), flashPageSize() * flashEraseBlockSize());
|
||||
|
||||
rp2040.resumeOtherCore();
|
||||
interrupts();
|
||||
}
|
||||
|
||||
void RP2040ArduinoPlatform::flashWritePage(uint16_t pageNumber, uint8_t* data)
|
||||
{
|
||||
noInterrupts();
|
||||
rp2040.idleOtherCore();
|
||||
|
||||
flash_range_program(KNX_FLASH_OFFSET + pageNumber * flashPageSize(), data, flashPageSize());
|
||||
|
||||
rp2040.resumeOtherCore();
|
||||
interrupts();
|
||||
}
|
||||
|
||||
void RP2040ArduinoPlatform::writeBufferedEraseBlock()
|
||||
{
|
||||
if(_bufferedEraseblockNumber > -1 && _bufferedEraseblockDirty)
|
||||
{
|
||||
noInterrupts();
|
||||
rp2040.idleOtherCore();
|
||||
|
||||
flash_range_erase (KNX_FLASH_OFFSET + _bufferedEraseblockNumber * flashPageSize() * flashEraseBlockSize(), flashPageSize() * flashEraseBlockSize());
|
||||
flash_range_program(KNX_FLASH_OFFSET + _bufferedEraseblockNumber * flashPageSize() * flashEraseBlockSize(), _eraseblockBuffer, flashPageSize() * flashEraseBlockSize());
|
||||
|
||||
rp2040.resumeOtherCore();
|
||||
interrupts();
|
||||
|
||||
_bufferedEraseblockDirty = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -4,17 +4,33 @@
|
||||
#include <knx/bits.h>
|
||||
|
||||
#include <Arduino.h>
|
||||
#ifdef USE_SAMD_EEPROM_EMULATION
|
||||
#include <FlashAsEEPROM.h>
|
||||
#endif
|
||||
|
||||
#if KNX_FLASH_SIZE % 1024
|
||||
#error "KNX_FLASH_SIZE must be multiple of 1024"
|
||||
#endif
|
||||
|
||||
#ifndef KNX_SERIAL
|
||||
#define KNX_SERIAL Serial1
|
||||
#endif
|
||||
|
||||
SamdPlatform::SamdPlatform()
|
||||
#ifndef KNX_NO_DEFAULT_UART
|
||||
: ArduinoPlatform(&Serial1)
|
||||
: ArduinoPlatform(&KNX_SERIAL)
|
||||
#endif
|
||||
{
|
||||
#ifndef USE_SAMD_EEPROM_EMULATION
|
||||
init();
|
||||
#endif
|
||||
}
|
||||
|
||||
SamdPlatform::SamdPlatform( HardwareSerial* s) : ArduinoPlatform(s)
|
||||
{
|
||||
#ifndef USE_SAMD_EEPROM_EMULATION
|
||||
init();
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32_t SamdPlatform::uniqueSerialNumber()
|
||||
@ -43,7 +59,9 @@ void SamdPlatform::restart()
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
|
||||
uint8_t * SamdPlatform::getEepromBuffer(uint16_t size)
|
||||
#ifdef USE_SAMD_EEPROM_EMULATION
|
||||
#pragma warning "Using EEPROM Simulation"
|
||||
uint8_t* SamdPlatform::getEepromBuffer(uint16_t size)
|
||||
{
|
||||
//EEPROM.begin(size);
|
||||
if(size > EEPROM_EMULATION_SIZE)
|
||||
@ -56,6 +74,156 @@ void SamdPlatform::commitToEeprom()
|
||||
{
|
||||
EEPROM.commit();
|
||||
}
|
||||
#else
|
||||
|
||||
extern uint32_t __etext;
|
||||
extern uint32_t __data_start__;
|
||||
extern uint32_t __data_end__;
|
||||
|
||||
static const uint32_t pageSizes[] = {8, 16, 32, 64, 128, 256, 512, 1024};
|
||||
|
||||
void SamdPlatform::init()
|
||||
{
|
||||
_memoryType = Flash;
|
||||
_pageSize = pageSizes[NVMCTRL->PARAM.bit.PSZ];
|
||||
_pageCnt = NVMCTRL->PARAM.bit.NVMP;
|
||||
_rowSize = PAGES_PER_ROW * _pageSize;
|
||||
|
||||
// find end of program flash and set limit to next row
|
||||
uint32_t endEddr = (uint32_t)(&__etext + (&__data_end__ - &__data_start__)); // text + data MemoryBlock
|
||||
_MemoryStart = getRowAddr(_pageSize * _pageCnt - KNX_FLASH_SIZE - 1); // 23295
|
||||
_MemoryEnd = getRowAddr(_pageSize * _pageCnt - 1);
|
||||
// chosen flash size is not available anymore
|
||||
if (_MemoryStart < endEddr) {
|
||||
println("KNX_FLASH_SIZE is not available (possible too much flash use by firmware)");
|
||||
fatalError();
|
||||
}
|
||||
}
|
||||
|
||||
size_t SamdPlatform::flashEraseBlockSize()
|
||||
{
|
||||
return PAGES_PER_ROW;
|
||||
}
|
||||
|
||||
size_t SamdPlatform::flashPageSize()
|
||||
{
|
||||
return _pageSize;
|
||||
}
|
||||
|
||||
uint8_t* SamdPlatform::userFlashStart()
|
||||
{
|
||||
return (uint8_t*)_MemoryStart;
|
||||
}
|
||||
|
||||
size_t SamdPlatform::userFlashSizeEraseBlocks()
|
||||
{
|
||||
if (KNX_FLASH_SIZE <= 0)
|
||||
return 0;
|
||||
else
|
||||
return ((KNX_FLASH_SIZE - 1) / (flashPageSize() * flashEraseBlockSize())) + 1;
|
||||
}
|
||||
|
||||
void SamdPlatform::flashErase(uint16_t eraseBlockNum)
|
||||
{
|
||||
noInterrupts();
|
||||
|
||||
eraseRow((void *)(_MemoryStart + eraseBlockNum * _rowSize));
|
||||
// flash_range_erase(KNX_FLASH_OFFSET + eraseBlockNum * flashPageSize() * flashEraseBlockSize(), flashPageSize() * flashEraseBlockSize());
|
||||
|
||||
interrupts();
|
||||
}
|
||||
|
||||
void SamdPlatform::flashWritePage(uint16_t pageNumber, uint8_t* data)
|
||||
{
|
||||
noInterrupts();
|
||||
|
||||
write((void *)(_MemoryStart + pageNumber * _pageSize), data, _pageSize);
|
||||
// flash_range_program(KNX_FLASH_OFFSET + pageNumber * flashPageSize(), data, flashPageSize());
|
||||
|
||||
interrupts();
|
||||
}
|
||||
|
||||
void SamdPlatform::writeBufferedEraseBlock()
|
||||
{
|
||||
if (_bufferedEraseblockNumber > -1 && _bufferedEraseblockDirty)
|
||||
{
|
||||
noInterrupts();
|
||||
|
||||
eraseRow((void *)(_MemoryStart + _bufferedEraseblockNumber * _rowSize));
|
||||
write((void *)(_MemoryStart + _bufferedEraseblockNumber * _rowSize), _eraseblockBuffer, _rowSize);
|
||||
// flash_range_erase(KNX_FLASH_OFFSET + _bufferedEraseblockNumber * flashPageSize() * flashEraseBlockSize(), flashPageSize() * flashEraseBlockSize());
|
||||
// flash_range_program(KNX_FLASH_OFFSET + _bufferedEraseblockNumber * flashPageSize() * flashEraseBlockSize(), _eraseblockBuffer, flashPageSize() * flashEraseBlockSize());
|
||||
|
||||
interrupts();
|
||||
|
||||
_bufferedEraseblockDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t SamdPlatform::getRowAddr(uint32_t flasAddr)
|
||||
{
|
||||
return flasAddr & ~(_rowSize - 1);
|
||||
}
|
||||
|
||||
void SamdPlatform::write(const volatile void *flash_ptr, const void *data, uint32_t size)
|
||||
{
|
||||
// Calculate data boundaries
|
||||
size = (size + 3) / 4;
|
||||
volatile uint32_t *src_addr = (volatile uint32_t *)data;
|
||||
volatile uint32_t *dst_addr = (volatile uint32_t *)flash_ptr;
|
||||
// volatile uint32_t *dst_addr = (volatile uint32_t *)flash_ptr;
|
||||
// const uint8_t *src_addr = (uint8_t *)data;
|
||||
|
||||
// Disable automatic page write
|
||||
NVMCTRL->CTRLB.bit.MANW = 1;
|
||||
|
||||
// Do writes in pages
|
||||
while (size)
|
||||
{
|
||||
// Execute "PBC" Page Buffer Clear
|
||||
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_PBC;
|
||||
while (NVMCTRL->INTFLAG.bit.READY == 0)
|
||||
{
|
||||
}
|
||||
|
||||
// Fill page buffer
|
||||
uint32_t i;
|
||||
for (i = 0; i < (_pageSize / 4) && size; i++)
|
||||
{
|
||||
*dst_addr = *src_addr;
|
||||
src_addr++;
|
||||
dst_addr++;
|
||||
size--;
|
||||
}
|
||||
|
||||
// Execute "WP" Write Page
|
||||
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_WP;
|
||||
while (NVMCTRL->INTFLAG.bit.READY == 0)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SamdPlatform::erase(const volatile void *flash_ptr, uint32_t size)
|
||||
{
|
||||
const uint8_t *ptr = (const uint8_t *)flash_ptr;
|
||||
while (size > _rowSize)
|
||||
{
|
||||
eraseRow(ptr);
|
||||
ptr += _rowSize;
|
||||
size -= _rowSize;
|
||||
}
|
||||
eraseRow(ptr);
|
||||
}
|
||||
|
||||
void SamdPlatform::eraseRow(const volatile void *flash_ptr)
|
||||
{
|
||||
NVMCTRL->ADDR.reg = ((uint32_t)flash_ptr) / 2;
|
||||
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER;
|
||||
while (!NVMCTRL->INTFLAG.bit.READY)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -4,9 +4,13 @@
|
||||
#include <EEPROM.h>
|
||||
#include "knx/bits.h"
|
||||
|
||||
#ifndef KNX_SERIAL
|
||||
#define KNX_SERIAL Serial2
|
||||
#endif
|
||||
|
||||
Stm32Platform::Stm32Platform()
|
||||
#ifndef KNX_NO_DEFAULT_UART
|
||||
: ArduinoPlatform(&Serial2)
|
||||
: ArduinoPlatform(&KNX_SERIAL)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
@ -32,16 +36,21 @@ void Stm32Platform::restart()
|
||||
|
||||
uint8_t * Stm32Platform::getEepromBuffer(uint16_t size)
|
||||
{
|
||||
if (size > E2END + 1)
|
||||
// check if the buffer already exists
|
||||
if (_eepromPtr == nullptr) // we need to initialize the buffer first
|
||||
{
|
||||
fatalError();
|
||||
if (size > E2END + 1)
|
||||
{
|
||||
fatalError();
|
||||
}
|
||||
|
||||
_eepromSize = size;
|
||||
_eepromPtr = new uint8_t[size];
|
||||
eeprom_buffer_fill();
|
||||
for (uint16_t i = 0; i < size; ++i)
|
||||
_eepromPtr[i] = eeprom_buffered_read_byte(i);
|
||||
}
|
||||
_eepromSize = size;
|
||||
delete [] _eepromPtr;
|
||||
_eepromPtr = new uint8_t[size];
|
||||
eeprom_buffer_fill();
|
||||
for (uint16_t i = 0; i < size; ++i)
|
||||
_eepromPtr[i] = eeprom_buffered_read_byte(i);
|
||||
|
||||
return _eepromPtr;
|
||||
}
|
||||
|
||||
@ -51,6 +60,11 @@ void Stm32Platform::commitToEeprom()
|
||||
return;
|
||||
for (uint16_t i = 0; i < _eepromSize; ++i)
|
||||
eeprom_buffered_write_byte(i, _eepromPtr[i]);
|
||||
// For some GD32 chips, the flash needs to be unlocked twice
|
||||
// and the first call will fail. If the first call is
|
||||
// successful, the second one (inside eeprom_buffer_flush)
|
||||
// does nothing.
|
||||
HAL_FLASH_Unlock();
|
||||
eeprom_buffer_flush();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user