From 87de2c8e008a36e529fa4bd15c51663af20466f3 Mon Sep 17 00:00:00 2001 From: Arkadiusz Materek Date: Thu, 3 Mar 2016 20:40:57 +0100 Subject: [PATCH] Initial implementation --- .cproject | 278 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .gitignore | 0 .project | 29 +++++++++++++++++++++++++++++ .settings/com.dave.mbs.xc800.prefs | 13 +++++++++++++ .settings/language.settings.xml | 23 +++++++++++++++++++++++ .settings/properties.index | 5 +++++ README.md | 7 ++++++- linker_script.ld | 241 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/dali/commands.hpp | 198 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/dali/config.hpp | 25 +++++++++++++++++++++++++ src/dali/controller/bus.cpp | 172 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/dali/controller/bus.hpp | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/dali/controller/initialization.cpp | 276 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/dali/controller/initialization.hpp | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/dali/controller/lamp.cpp | 293 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/dali/controller/lamp.hpp | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/dali/controller/lamp_helper.cpp | 341 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/dali/controller/lamp_helper.hpp | 28 ++++++++++++++++++++++++++++ src/dali/controller/memory.cpp | 429 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/dali/controller/memory.hpp | 196 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/dali/controller/query_store.cpp | 245 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/dali/controller/query_store.hpp | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/dali/dali.hpp | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/dali/slave.cpp | 509 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/dali/slave.hpp | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/util/manchester.cpp | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/util/manchester.hpp | 23 +++++++++++++++++++++++ src/xmc1200/bccu.cpp | 229 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/xmc1200/bccu.hpp | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/xmc1200/bccu_config.c | 42 ++++++++++++++++++++++++++++++++++++++++++ src/xmc1200/bccu_config.h | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/xmc1200/clock.cpp | 31 +++++++++++++++++++++++++++++++ src/xmc1200/clock.hpp | 41 +++++++++++++++++++++++++++++++++++++++++ src/xmc1200/dali/bus.cpp | 384 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/xmc1200/dali/bus.hpp | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ src/xmc1200/dali/bus_config.c | 41 +++++++++++++++++++++++++++++++++++++++++ src/xmc1200/dali/bus_config.h | 37 +++++++++++++++++++++++++++++++++++++ src/xmc1200/dali/lamp.cpp | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/xmc1200/dali/lamp.hpp | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/xmc1200/dali/memory.cpp | 392 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/xmc1200/dali/memory.hpp | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ src/xmc1200/dali/memory_config.hpp | 17 +++++++++++++++++ src/xmc1200/dali/timer.cpp | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/xmc1200/dali/timer.hpp | 45 +++++++++++++++++++++++++++++++++++++++++++++ src/xmc1200/main.cpp | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/xmc1200/startup_XMC1200.S | 485 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/xmc1200/system_XMC1200.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 47 files changed, 6438 insertions(+), 1 deletion(-) create mode 100644 .cproject create mode 100644 .gitignore create mode 100644 .project create mode 100644 .settings/com.dave.mbs.xc800.prefs create mode 100644 .settings/language.settings.xml create mode 100644 .settings/properties.index create mode 100644 linker_script.ld create mode 100644 src/dali/commands.hpp create mode 100644 src/dali/config.hpp create mode 100644 src/dali/controller/bus.cpp create mode 100644 src/dali/controller/bus.hpp create mode 100644 src/dali/controller/initialization.cpp create mode 100644 src/dali/controller/initialization.hpp create mode 100644 src/dali/controller/lamp.cpp create mode 100644 src/dali/controller/lamp.hpp create mode 100644 src/dali/controller/lamp_helper.cpp create mode 100644 src/dali/controller/lamp_helper.hpp create mode 100644 src/dali/controller/memory.cpp create mode 100644 src/dali/controller/memory.hpp create mode 100644 src/dali/controller/query_store.cpp create mode 100644 src/dali/controller/query_store.hpp create mode 100644 src/dali/dali.hpp create mode 100644 src/dali/slave.cpp create mode 100644 src/dali/slave.hpp create mode 100644 src/util/manchester.cpp create mode 100644 src/util/manchester.hpp create mode 100644 src/xmc1200/bccu.cpp create mode 100644 src/xmc1200/bccu.hpp create mode 100644 src/xmc1200/bccu_config.c create mode 100644 src/xmc1200/bccu_config.h create mode 100644 src/xmc1200/clock.cpp create mode 100644 src/xmc1200/clock.hpp create mode 100644 src/xmc1200/dali/bus.cpp create mode 100644 src/xmc1200/dali/bus.hpp create mode 100644 src/xmc1200/dali/bus_config.c create mode 100644 src/xmc1200/dali/bus_config.h create mode 100644 src/xmc1200/dali/lamp.cpp create mode 100644 src/xmc1200/dali/lamp.hpp create mode 100644 src/xmc1200/dali/memory.cpp create mode 100644 src/xmc1200/dali/memory.hpp create mode 100644 src/xmc1200/dali/memory_config.hpp create mode 100644 src/xmc1200/dali/timer.cpp create mode 100644 src/xmc1200/dali/timer.hpp create mode 100644 src/xmc1200/main.cpp create mode 100644 src/xmc1200/startup_XMC1200.S create mode 100644 src/xmc1200/system_XMC1200.c diff --git a/.cproject b/.cproject new file mode 100644 index 0000000..6cc94b6 --- /dev/null +++ b/.cproject @@ -0,0 +1,278 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/.gitignore diff --git a/.project b/.project new file mode 100644 index 0000000..36ba0c5 --- /dev/null +++ b/.project @@ -0,0 +1,29 @@ + + + DALI + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + com.ifx.xmc4000.xmc4000Nature + com.dave.common.daveBenchNature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + org.eclipse.cdt.core.ccnature + + diff --git a/.settings/com.dave.mbs.xc800.prefs b/.settings/com.dave.mbs.xc800.prefs new file mode 100644 index 0000000..b0d2b88 --- /dev/null +++ b/.settings/com.dave.mbs.xc800.prefs @@ -0,0 +1,13 @@ +ACTIVE_CONFIG_NAME=Debug +AppCompatibilitySet=1 +DEVICE_DESC=Package\= TSSOP38 \nROM\= 200 KB Flash \nRAM\= 16 KB RAM \nInOut\= 34 digital I/O \nADC\= 12 ADC Channels, 12-bit, Analog-to-Digital Converter \nTimed_InOut\= 6 Timer, 4 CAPCOM channels \nSerial\= 2 USIC channels \nSHS\= 2 Sample and Hold Sequencer \nCOMP\= Comparator Control Unit \nTouch\= Touch and LED matrix control \nBCCU\= Brightness and Color Control Unit \n +DEVICE_NAME=XMC1200-T038x0200 +DEVICE_PACKAGE=TSSOP38 +DEVICE_PACK_VERSION=2.0.0 +DEVICE_PATH=/DeviceRoot/Microcontrollers/XMC1000/XMC1200 Series/XMC1200-T038x0200 +FLASH_SIZE=200 +MBS_PROVIDER_ID_KEY=com.dave.mbs.xmc4000.xmc4000MbsFactory +SOFTWARE_ID=XMC1.2.00.T038.ALL +TEMPLATE_KEY=com.ifx.xmc4000.appEmptyMainTemplate +eclipse.preferences.version=1 +minDaveVersion=4.1.2 diff --git a/.settings/language.settings.xml b/.settings/language.settings.xml new file mode 100644 index 0000000..5bc6ec3 --- /dev/null +++ b/.settings/language.settings.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.settings/properties.index b/.settings/properties.index new file mode 100644 index 0000000..1f7acaf --- /dev/null +++ b/.settings/properties.index @@ -0,0 +1,5 @@ + + +
+
+ \ No newline at end of file diff --git a/README.md b/README.md index 224f14c..8593882 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,7 @@ # dali_slave -Implementation of DALI slave. +Implementation of DALI slave using C++ 11. + +Requirements: +* DAVE4 +* Infineon libraries "Infineon/Libraries" +* LED Lighting Application Kit \ No newline at end of file diff --git a/linker_script.ld b/linker_script.ld new file mode 100644 index 0000000..92c412c --- /dev/null +++ b/linker_script.ld @@ -0,0 +1,241 @@ +/** + * @file XMC1200x0200.ld + * @date 2015-07-07 + * + * @cond + ********************************************************************************************************************* + * Linker file for the GNU C Compiler v1.7 + * Supported devices: XMC1200-T038F0200 + * XMC1201-T038F0200 + * XMC1201-Q040F0200 + * + * Copyright (c) 2015, Infineon Technologies AG + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification,are permitted provided that the + * following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following + * disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided with the distribution. + * + * Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY,OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * To improve the quality of the software, users are encouraged to share modifications, enhancements or bug fixes with + * Infineon Technologies AG dave@infineon.com). + ********************************************************************************************************************* + * + * Change History + * -------------- + * + * 2015-07-07: + * - Product splitting + * - Copyright notice update + * + * @endcond + * + */ + +OUTPUT_FORMAT("elf32-littlearm") +OUTPUT_ARCH(arm) +ENTRY(Reset_Handler) + +MEMORY +{ + FLASH(RX) : ORIGIN = 0x10001000, LENGTH = 0x31a00 + SRAM(!RX) : ORIGIN = 0x20000000, LENGTH = 0x4000 +} + +stack_size = 1536; + +SECTIONS +{ + /* TEXT section */ + + .text : + { + sText = .; + KEEP(*(.reset)); + *(.text .text.* .gnu.linkonce.t.*); + + /* ARM <->THUMB interworking */ + *(.glue*) + *(.v4*) + *(.vfp11_veneer) + + /* C++ Support */ + KEEP(*(.init)) + __preinit_array_start = .; + KEEP (*(.preinit_array)) + __preinit_array_end = .; + __init_array_start = .; + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + __init_array_end = .; + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*crtend.o(.ctors)) + KEEP(*(.fini)) + __fini_array_start = .; + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + __fini_array_end = .; + + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*crtend.o(.dtors)) + + /* Exception handling support */ + __extab_start = .; + *(.ARM.extab* .gnu.linkonce.armextab.*) + . = ALIGN(4); + __extab_end = ABSOLUTE(.); + } > FLASH + + /* Exception handling, exidx needs a dedicated section */ + .ARM.exidx : + { + __exidx_start = .; + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + . = ALIGN(4); + __exidx_end = ABSOLUTE(.); + } > FLASH + + /* CONST data section */ + .rodata : + { + *(.rodata .rodata.*) + *(.gnu.linkonce.r*) + } > FLASH + + . = ALIGN(16); + + /* End of RO-DATA and start of LOAD region for the veneers */ + eROData = . ; + + /* DSRAM layout (Lowest to highest)*/ + /* Veneer <-> Stack <-> DATA <-> BSS <-> HEAP */ + + .VENEER_Code ABSOLUTE(0x2000000C): AT(eROData) + { + VeneerStart = .; + KEEP(*(.XmcVeneerCode)); + . = ALIGN(4); + VeneerEnd = .; + } > SRAM + + VeneerSize = ABSOLUTE(VeneerEnd) - ABSOLUTE(VeneerStart); + + /* Dummy section for stack */ + Stack (NOLOAD) : AT(0) + { + . = ALIGN(8); + . = . + stack_size; + __initial_sp = .; + } > SRAM + + /* Standard DATA and user defined DATA/BSS/CONST sections */ + DataLoadAddr = eROData + VeneerSize; + .data : AT(DataLoadAddr) + { + __data_start = .; + * (.data); + * (.data*); + *(*.data); + *(.gnu.linkonce.d*) + . = ALIGN(4); + __data_end = .; + } > SRAM + __data_size = __data_end - __data_start; + + .ram_code : AT(DataLoadAddr + __data_size) + { + __ram_code_start = .; + /* functions with __attribute__ ((section (".ram_code")))*/ + *(.ram_code) + . = ALIGN(4); + __ram_code_end = .; + } > SRAM + __ram_code_load = LOADADDR (.ram_code); + __ram_code_size = __ram_code_end - __ram_code_start; + + /* BSS section */ + .bss (NOLOAD) : + { + __bss_start = .; + * (.bss); + * (.bss*); + * (COMMON); + *(.gnu.linkonce.b*) + . = ALIGN(4); + __bss_end = .; + . = ALIGN(8); + Heap_Bank1_Start = .; + } > SRAM + __bss_size = __bss_end - __bss_start; + + /* .no_init section */ + .no_init 0x20003FFC (NOLOAD) : + { + Heap_Bank1_End = .; + * (.no_init); + } > SRAM + + /* Heap - Bank1*/ + Heap_Bank1_Size = Heap_Bank1_End - Heap_Bank1_Start; + + /DISCARD/ : + { + *(.comment) + } + + .stab 0 (NOLOAD) : { *(.stab) } + .stabstr 0 (NOLOAD) : { *(.stabstr) } + + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + .debug_pubtypes 0 : { *(.debug_pubtypes) } + + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + + /* DWARF 2.1 */ + .debug_ranges 0 : { *(.debug_ranges) } + + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + + /* Build attributes */ + .build_attributes 0 : { *(.ARM.attributes) } +} diff --git a/src/dali/commands.hpp b/src/dali/commands.hpp new file mode 100644 index 0000000..583214d --- /dev/null +++ b/src/dali/commands.hpp @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2015-2016, Arkadiusz Materek (arekmat@poczta.fm) + * + * Licensed under GNU General Public License 3.0 or later. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef DALI_COMMANDS_HPP_ +#define DALI_COMMANDS_HPP_ + +#include + +namespace dali { + +enum class Command { + // Power control commands + OFF = 0, + UP = 1, + DOWN = 2, + STEP_UP = 3, + STEP_DOWN = 4, + RECALL_MAX_LEVEL = 5, + RECALL_MIN_LEVEL = 6, + STEP_DOWN_AND_OFF = 7, + ON_AND_STEP_UP = 8, + + ENABLE_DAPC_SEQUENCE = 9, + + GO_TO_SCENE_0 = 16, + GO_TO_SCENE_1 = 17, + GO_TO_SCENE_2 = 18, + GO_TO_SCENE_3 = 19, + GO_TO_SCENE_4 = 20, + GO_TO_SCENE_5 = 21, + GO_TO_SCENE_6 = 22, + GO_TO_SCENE_7 = 23, + GO_TO_SCENE_8 = 24, + GO_TO_SCENE_9 = 25, + GO_TO_SCENE_A = 26, + GO_TO_SCENE_B = 27, + GO_TO_SCENE_C = 28, + GO_TO_SCENE_D = 29, + GO_TO_SCENE_E = 30, + GO_TO_SCENE_F = 31, + RESET = 32, + STORE_ACTUAL_LEVEL_IN_DTR = 33, + STORE_DTR_AS_MAX_LEVEL = 42, + STORE_DTR_AS_MIN_LEVEL = 43, + STORE_DTR_AS_SYS_FAIL_LEVEL = 44, + STORE_DTR_AS_POWER_ON_LEVEL = 45, + STORE_DTR_AS_FADE_TIME = 46, + STORE_DTR_AS_FADE_RATE = 47, + STORE_DTR_AS_SCENE_0 = 64, + STORE_DTR_AS_SCENE_1 = 65, + STORE_DTR_AS_SCENE_2 = 66, + STORE_DTR_AS_SCENE_3 = 67, + STORE_DTR_AS_SCENE_4 = 68, + STORE_DTR_AS_SCENE_5 = 69, + STORE_DTR_AS_SCENE_6 = 70, + STORE_DTR_AS_SCENE_7 = 71, + STORE_DTR_AS_SCENE_8 = 72, + STORE_DTR_AS_SCENE_9 = 73, + STORE_DTR_AS_SCENE_A = 74, + STORE_DTR_AS_SCENE_B = 75, + STORE_DTR_AS_SCENE_C = 76, + STORE_DTR_AS_SCENE_D = 77, + STORE_DTR_AS_SCENE_E = 78, + STORE_DTR_AS_SCENE_F = 79, + REMOVE_FROM_SCENE_0 = 80, // to 95 + REMOVE_FROM_SCENE_1 = 81, + REMOVE_FROM_SCENE_2 = 82, + REMOVE_FROM_SCENE_3 = 83, + REMOVE_FROM_SCENE_4 = 84, + REMOVE_FROM_SCENE_5 = 85, + REMOVE_FROM_SCENE_6 = 86, + REMOVE_FROM_SCENE_7 = 87, + REMOVE_FROM_SCENE_8 = 88, + REMOVE_FROM_SCENE_9 = 89, + REMOVE_FROM_SCENE_A = 90, + REMOVE_FROM_SCENE_B = 91, + REMOVE_FROM_SCENE_C = 92, + REMOVE_FROM_SCENE_D = 93, + REMOVE_FROM_SCENE_E = 94, + REMOVE_FROM_SCENE_F = 95, + ADD_TO_GROUP_0 = 96, + ADD_TO_GROUP_1 = 97, + ADD_TO_GROUP_2 = 98, + ADD_TO_GROUP_3 = 99, + ADD_TO_GROUP_4 = 100, + ADD_TO_GROUP_5 = 101, + ADD_TO_GROUP_6 = 102, + ADD_TO_GROUP_7 = 103, + ADD_TO_GROUP_8 = 104, + ADD_TO_GROUP_9 = 105, + ADD_TO_GROUP_A = 106, + ADD_TO_GROUP_B = 107, + ADD_TO_GROUP_C = 108, + ADD_TO_GROUP_D = 109, + ADD_TO_GROUP_E = 110, + ADD_TO_GROUP_F = 111, + REMOVE_FROM_GROUP_0 = 112, + REMOVE_FROM_GROUP_1 = 113, + REMOVE_FROM_GROUP_2 = 114, + REMOVE_FROM_GROUP_3 = 115, + REMOVE_FROM_GROUP_4 = 116, + REMOVE_FROM_GROUP_5 = 117, + REMOVE_FROM_GROUP_6 = 118, + REMOVE_FROM_GROUP_7 = 119, + REMOVE_FROM_GROUP_8 = 120, + REMOVE_FROM_GROUP_9 = 121, + REMOVE_FROM_GROUP_A = 122, + REMOVE_FROM_GROUP_B = 123, + REMOVE_FROM_GROUP_C = 124, + REMOVE_FROM_GROUP_D = 125, + REMOVE_FROM_GROUP_E = 126, + REMOVE_FROM_GROUP_F = 127, + STORE_DTR_AS_SHORT_ADDR = 128, + ENABLE_WRITE_MEMORY = 129, + + // Query commands + QUERY_STATUS = 144, + QUERY_CONTROL_GEAR = 145, + QUERY_LAMP_FAILURE = 146, + QUERY_LAMP_POWER_ON = 147, + QUERY_LIMIT_ERROR = 148, + QUERY_RESET_STATE = 149, + QUERY_MISSING_SHORT_ADDR = 150, + QUERY_VERSION_NUMBER = 151, + QUERY_CONTENT_DTR = 152, + QUERY_DEVICE_TYPE = 153, + QUERY_PHISICAL_MIN_LEVEL = 154, + QUERY_POWER_FAILURE = 155, + QUERY_CONTENT_DTR1 = 156, + QUERY_CONTENT_DTR2 = 157, + + QUERY_ACTUAL_LEVEL = 160, + QUERY_MAX_LEVEL = 161, + QUERY_MIN_LEVEL = 162, + QUERY_POWER_ON_LEVEL = 163, + QUERY_SYS_FAILURE_LEVEL = 164, + QUERY_FADE_TIME_OR_RATE = 165, + QUERY_SCENE_0_LEVEL = 176, // to 191 + QUERY_SCENE_1_LEVEL = 177, + QUERY_SCENE_2_LEVEL = 178, + QUERY_SCENE_3_LEVEL = 179, + QUERY_SCENE_4_LEVEL = 180, + QUERY_SCENE_5_LEVEL = 181, + QUERY_SCENE_6_LEVEL = 182, + QUERY_SCENE_7_LEVEL = 183, + QUERY_SCENE_8_LEVEL = 184, + QUERY_SCENE_9_LEVEL = 185, + QUERY_SCENE_A_LEVEL = 186, + QUERY_SCENE_B_LEVEL = 187, + QUERY_SCENE_C_LEVEL = 188, + QUERY_SCENE_D_LEVEL = 189, + QUERY_SCENE_E_LEVEL = 190, + QUERY_SCENE_F_LEVEL = 191, + QUERY_GROUPS_L = 192, + QUERY_GROUPS_H = 193, + QUERY_RANDOM_ADDR_H = 194, + QUERY_RANDOM_ADDR_M = 195, + QUERY_RANDOM_ADDR_L = 196, + READ_MEMORY_LOCATION = 197, + + QUERY_EXTENDED_VERSION_NUMBER = 255, + + // Special commands + _SPECIAL_COMMAND = 1024, // not for use + + DIRECT_POWER_CONTROL = _SPECIAL_COMMAND, + TERMINATE = _SPECIAL_COMMAND + 161, // 256 + DATA_TRANSFER_REGISTER = _SPECIAL_COMMAND + 163, // 257 + INITIALISE = _SPECIAL_COMMAND + 165, // 258 + RANDOMISE = _SPECIAL_COMMAND + 167, // 259 + COMPARE = _SPECIAL_COMMAND + 169, // 260 + WITHDRAW = _SPECIAL_COMMAND + 171, // 261 + SEARCHADDRH = _SPECIAL_COMMAND + 177, // 264 + SEARCHADDRM = _SPECIAL_COMMAND + 179, // 265 + SEARCHADDRL = _SPECIAL_COMMAND + 181, // 266, + PROGRAM_SHORT_ADDRESS = _SPECIAL_COMMAND + 183, // 267 + VERIFY_SHORT_ADDRESS = _SPECIAL_COMMAND + 185, // 268 + QUERY_SHORT_ADDRESS = _SPECIAL_COMMAND + 187, // 269 + PHYSICAL_SELECTION = _SPECIAL_COMMAND + 189, // 270 + ENABLE_DEVICE_TYPE_X = _SPECIAL_COMMAND + 193, // 272 + DATA_TRANSFER_REGISTER_1 = _SPECIAL_COMMAND + 195, // 273 + DATA_TRANSFER_REGISTER_2 = _SPECIAL_COMMAND + 197, // 274 + WRITE_MEMORY_LOCATION = _SPECIAL_COMMAND + 199, // 275 + + INVALID = 0xffff, +}; + +} +// namespace dali + +#endif // DALI_COMMANDS_HPP_ diff --git a/src/dali/config.hpp b/src/dali/config.hpp new file mode 100644 index 0000000..d84351e --- /dev/null +++ b/src/dali/config.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2015-2016, Arkadiusz Materek (arekmat@poczta.fm) + * + * Licensed under GNU General Public License 3.0 or later. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef DALI_CONFIG_H_ +#define DALI_CONFIG_H_ + +#define DALI_VERSION 1 +#define DALI_DEVICE_TYPE 8 + +#define DALI_BANKS 3 +#define DALI_BANK0_ADDR 0 // Bank 0 (16 bytes) according to 62386-102 +#define DALI_BANK1_ADDR 16 // Bank 1 (16 bytes) according to 62386-102 +#define DALI_BANK2_ADDR 32 // Bank 2 (28 bytes) data 62386-102 (26 bytes) +#define DALI_BANK3_ADDR 60 + +#define DALI_PHISICAL_MIN_LEVEL 1 + +#endif // DALI_CONFIG_H_ diff --git a/src/dali/controller/bus.cpp b/src/dali/controller/bus.cpp new file mode 100644 index 0000000..b2a00c1 --- /dev/null +++ b/src/dali/controller/bus.cpp @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2015-2016, Arkadiusz Materek (arekmat@poczta.fm) + * + * Licensed under GNU General Public License 3.0 or later. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "bus.hpp" + +#include + +namespace dali { +namespace controller { +namespace { + +const uint64_t kCommandRepeatTimeout = 100; + +} + +Bus::Bus(IBus* bus) : + mBus(bus), + mState(IBus::IBusState::UNKNOWN), + mListener(nullptr), + mLastCommand(Command::INVALID), + mCommandRepeatCount(0), + mLastCommandTimeMs(0) { + mBus->registerClient(this); +} + +Bus::~Bus() { + mBus->unregisterClient(this); +} + +void Bus::setListener(Listener* listener) { + mListener = listener; +} + +void Bus::onDataReceived(uint64_t timeMs, uint16_t data) { + Command command; + uint8_t param; + if (filterAddress(data, &command, ¶m) != Status::OK) { + return; // Status::INVALID; + } + if (command != Command::INVALID) { // should be always true + if (mLastCommand == Command::INVALID) { + mCommandRepeatCount = 0; + mLastCommandTimeMs = timeMs; + Status status = mListener->handleCommand(mCommandRepeatCount, command, param); + if (status == Status::REPEAT_REQUIRED) { + mLastCommand = command; + return; // Status::OK; + } else { + mLastCommand = Command::INVALID; + return; // status; + } + } else if (mLastCommand == command) { + if (timeMs - mLastCommandTimeMs < kCommandRepeatTimeout) { + mCommandRepeatCount++; + } else { + mLastCommand = Command::INVALID; + mCommandRepeatCount = 0; + } + mLastCommandTimeMs = timeMs; + Status status = mListener->handleCommand(mCommandRepeatCount, command, param); + if (status == Status::REPEAT_REQUIRED) { + mLastCommand = command; + return; // Status::OK; + } else { + mLastCommand = Command::INVALID; + return; // status; + } + } else { // (mLastCommand != Command::INVALID) || (mLastCommand != cmd) + mLastCommand = Command::INVALID; + mCommandRepeatCount = 0; + if (timeMs - mLastCommandTimeMs < kCommandRepeatTimeout) { + mLastCommandTimeMs = timeMs; + mListener->handleIgnoredCommand(command, param); + } else { + mLastCommandTimeMs = timeMs; + Status status = mListener->handleCommand(mCommandRepeatCount, command, param); + if (status == Status::REPEAT_REQUIRED) { + mLastCommand = command; + return; // Status::OK; + } else { + return; // status; + } + } + } + } + return; // Status::INVALID; +} + +void Bus::onBusStateChanged(IBus::IBusState state) { + if (mState != state) { + mState = state; + if (state == IBus::IBusState::DISCONNECTED) { + mListener->onBusDisconnected(); + } + } +} + +Status Bus::sendAck(uint8_t ack) { + return mBus->sendAck(ack); +} + +Status Bus::filterAddress(uint16_t data, Command* command, uint8_t* param) { + *param = (uint8_t) (data & 0xff); + *command = Command::INVALID; + uint8_t addr = (uint8_t) (data >> 8); + bool cmdBitSet = ((addr & 0x01) != 0); + if ((addr & 0xfe) == 0xfe) { + // Broadcast + if (cmdBitSet) { + *command = (Command) *param; + *param = 0xff; + } else { + *command = Command::DIRECT_POWER_CONTROL; + } + } else if ((addr & 0x80) == 0) { + // Short address + addr >>= 1; + uint8_t myAddr = mListener->getShortAddr() >> 1; + if (cmdBitSet) { + *command = (Command) *param; + *param = 0xff; + } else { + *command = Command::DIRECT_POWER_CONTROL; + } + if (addr != myAddr) { + return Status::INVALID; + } + } else if ((addr & 0x60) == 0) { + // Group address + uint8_t group = (addr >> 1) & 0x0f; + uint16_t groups = mListener->getGroups(); + if (cmdBitSet) { + *command = (Command) *param; + *param = 0xff; + } else { + *command = Command::DIRECT_POWER_CONTROL; + } + if ((groups & (1 << group)) == 0) { + return Status::INVALID; + } + } else { + *command = (Command) ((uint16_t) Command::_SPECIAL_COMMAND + addr); + if (*command == Command::INITIALISE) { + uint8_t myAddr = mListener->getShortAddr() >> 1; + switch (*param) { + case 0x00: // All control gear shall react + break; + case 0xff: // Control gear without short address shall react + if (myAddr <= DALI_ADDR_MAX) { + return Status::INVALID; + } + break; + default: // Control gear with the address AAAAAAb shall react + if (myAddr != (*param >> 1)) { + return Status::INVALID; + } + break; + } + } + } + return Status::OK; +} + +} // namespace controller +}// namespace dali diff --git a/src/dali/controller/bus.hpp b/src/dali/controller/bus.hpp new file mode 100644 index 0000000..0827cf6 --- /dev/null +++ b/src/dali/controller/bus.hpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2015-2016, Arkadiusz Materek (arekmat@poczta.fm) + * + * Licensed under GNU General Public License 3.0 or later. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef DALI_BUS_CONTROLLER_H_ +#define DALI_BUS_CONTROLLER_H_ + +#include + +namespace dali { +namespace controller { + +class Bus: public IBus::IBusClient { +public: + + class Listener { + public: + virtual uint8_t getShortAddr() = 0; + virtual uint16_t getGroups() = 0; + virtual Status handleCommand(uint16_t repeat, Command cmd, uint8_t param) = 0; + virtual Status handleIgnoredCommand(Command cmd, uint8_t param) = 0; + virtual void onBusDisconnected() = 0; + }; + + explicit Bus(IBus* bus); + virtual ~Bus(); + + void setListener(Listener* listener); + + void onDataReceived(uint64_t timeMs, uint16_t data) override; + void onBusStateChanged(IBus::IBusState state) override; + + Status sendAck(uint8_t ack); + uint64_t getLastCommandTimeMs() { + return mLastCommandTimeMs; + } + +private: + Bus(const Bus& other) = delete; + Bus& operator=(const Bus&) = delete; + + Status filterAddress(uint16_t data, Command* command, uint8_t* param); + + IBus* const mBus; + IBus::IBusState mState; + Listener* mListener; + Command mLastCommand; + uint16_t mCommandRepeatCount; + uint64_t mLastCommandTimeMs; +}; + +} // namespace controller +} // namespace dali + +#endif // DALI_BUS_CONTROLLER_H_ diff --git a/src/dali/controller/initialization.cpp b/src/dali/controller/initialization.cpp new file mode 100644 index 0000000..b721ca6 --- /dev/null +++ b/src/dali/controller/initialization.cpp @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2015-2016, Arkadiusz Materek (arekmat@poczta.fm) + * + * Licensed under GNU General Public License 3.0 or later. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "initialization.hpp" + +#include "bus.hpp" +#include "memory.hpp" + +namespace dali { +namespace controller { + +Initialization::Initialization(ITimer* timer, Memory* memoryController) : + mTimer(timer), + mMemoryController(memoryController), + mInitializeTime(0), + mInitialized(false), + mWithdraw(false), + mSelected(Selection::NONE) { +} + +Status Initialization::initialize(uint8_t param) { + checkOperatingTimeout(); + + if (!mInitialized) { + mInitialized = true; + operatingTimeStart(); + return Status::OK; + } else { + return Status::INVALID_STATE; + } +} + +Status Initialization::randomize() { + checkOperatingTimeout(); + + uint32_t randomAddr = mTimer->randomize(); + randomAddr &= 0x00ffffff; + mMemoryController->setRandomAddr(randomAddr); + return Status::OK; +} + +Status Initialization::compare() { + checkOperatingTimeout(); + + if (mInitialized) { + switch (mSelected) { + case Selection::NONE: + case Selection::SELECTED: { + if (mWithdraw) { + return Status::INVALID; + } + uint32_t searchAddr = mMemoryController->getSearchAddr(); + uint32_t randomAddr = mMemoryController->getRandomAddr(); + if (randomAddr <= searchAddr) { + return Status::OK; + } + return Status::INVALID; + } + + default: + return Status::INVALID_STATE; + } + } + return Status::INVALID_STATE; +} + +Status Initialization::withdraw() { + checkOperatingTimeout(); + + if (mInitialized) { + switch (mSelected) { + case Selection::NONE: + case Selection::SELECTED: { + uint32_t searchAddr = mMemoryController->getSearchAddr(); + uint32_t randomAddr = mMemoryController->getRandomAddr(); + if (searchAddr == randomAddr) { + mWithdraw = true; + } + return Status::OK; + } + + default: + return Status::INVALID_STATE; + } + } + return Status::INVALID_STATE; +} + +Status Initialization::searchAddrH(uint8_t addr) { + checkOperatingTimeout(); + + if (mInitialized) { + return setSearchAddr(addr, 16); + } + return Status::INVALID_STATE; +} + +Status Initialization::searchAddrM(uint8_t addr) { + checkOperatingTimeout(); + + if (mInitialized) { + return setSearchAddr(addr, 8); + } + return Status::INVALID_STATE; +} + +Status Initialization::searchAddrL(uint8_t addr) { + checkOperatingTimeout(); + + if (mInitialized) { + return setSearchAddr(addr, 0); + } + return Status::INVALID_STATE; +} + +Status Initialization::programShortAddr(uint8_t addr) { + checkOperatingTimeout(); + + if ((addr != DALI_MASK) && ((addr >> 1) > DALI_ADDR_MAX)) { + return Status::ERROR; + } + + if (mInitialized) { + switch (mSelected) { + case Selection::NONE: + case Selection::SELECTED: { + uint32_t searchAddr = mMemoryController->getSearchAddr(); + uint32_t randomAddr = mMemoryController->getRandomAddr(); + if (searchAddr == randomAddr) { + return mMemoryController->setShortAddr(addr); + } + return Status::OK; + } + + case Selection::PHYSICAL_SELECTED: + return mMemoryController->setShortAddr(addr); + + default: + return Status::INVALID_STATE; + } + } + return Status::INVALID_STATE; +} + +Status Initialization::verifySortAddr(uint8_t addr) { + checkOperatingTimeout(); + + if (mInitialized) { + uint8_t myAddr = mMemoryController->getShortAddr(); + if (myAddr == addr) { + return Status::OK; + } + return Status::INVALID; + } + return Status::INVALID_STATE; +} + +Status Initialization::queryShortAddr(uint8_t* addr) { + checkOperatingTimeout(); + + if (mInitialized) { + switch (mSelected) { + case Selection::NONE: + case Selection::SELECTED: { + uint32_t searchAddr = mMemoryController->getSearchAddr(); + uint32_t randomAddr = mMemoryController->getRandomAddr(); + if (searchAddr == randomAddr) { + *addr = mMemoryController->getShortAddr(); + return Status::OK; + } + return Status::INVALID; + } + + case Selection::PHYSICAL_SELECTED: { + *addr = mMemoryController->getShortAddr(); + return Status::OK; + } + + default: + return Status::INVALID_STATE; + } + } + return Status::INVALID_STATE; +} + +Status Initialization::physicalSelection() { + checkOperatingTimeout(); + + if (mInitialized) { + switch (mSelected) { + case Selection::NONE: + mSelected = Selection::SELECTED; + break; + + case Selection::PHYSICAL_SELECTED: + mSelected = Selection::SELECTED; + break; + + default: + mSelected = Selection::NONE; + break; + } + return Status::OK; + } + return Status::INVALID_STATE; +} + +Status Initialization::terminate() { + if (mInitialized) { + operatingTimeStop(); + mInitialized = false; + mWithdraw = false; + mSelected = Selection::NONE; + return Status::OK; + } + return Status::INVALID_STATE; +} + +void Initialization::reset() { + checkOperatingTimeout(); + // TODO check what should be reset + mSelected = Selection::NONE; + mWithdraw = false; +} + +void Initialization::onLampStateChnaged(ILamp::ILampState state) { + switch (state) { + case ILamp::ILampState::DISCONNECTED: + if (mSelected == Selection::SELECTED) { + mSelected = Selection::PHYSICAL_SELECTED; + } + break; + + case ILamp::ILampState::OK: + if (mSelected == Selection::PHYSICAL_SELECTED) { + mSelected = Selection::SELECTED; + } + break; + + default: + break; + } +} + +Status Initialization::setSearchAddr(uint8_t addr, uint8_t offset) { + uint32_t searchAddr = mMemoryController->getSearchAddr(); + searchAddr &= ~((uint32_t) 0xff << offset); + searchAddr |= (uint32_t) addr << offset; + mMemoryController->setSearchAddr(searchAddr); + return Status::OK; +} + +void Initialization::operatingTimeStart() { + mInitializeTime = mTimer->getTime(); + mInitializeTime += 1000 * 60 * 60 * 15; // 15min +} + +void Initialization::operatingTimeStop() { + mInitializeTime = 0; +} + +void Initialization::checkOperatingTimeout() { + if ((mInitializeTime != 0) && (mTimer->getTime() > mInitializeTime)) { + terminate(); + } +} + +} // namespace controller +} // namespace dali diff --git a/src/dali/controller/initialization.hpp b/src/dali/controller/initialization.hpp new file mode 100644 index 0000000..747fa6c --- /dev/null +++ b/src/dali/controller/initialization.hpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2015-2016, Arkadiusz Materek (arekmat@poczta.fm) + * + * Licensed under GNU General Public License 3.0 or later. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef DALI_CONFIGURATION_CONTROLER_H_ +#define DALI_CONFIGURATION_CONTROLER_H_ + +#include + +namespace dali { +namespace controller { + +class Memory; +class Bus; + +class Initialization { +public: + explicit Initialization(ITimer* timer, Memory* memoryController); + virtual ~Initialization() {} + + Status initialize(uint8_t param); + Status randomize(); + Status compare(); + Status withdraw(); + Status searchAddrH(uint8_t addr); + Status searchAddrM(uint8_t addr); + Status searchAddrL(uint8_t addr); + Status programShortAddr(uint8_t addr); + Status verifySortAddr(uint8_t addr); + Status queryShortAddr(uint8_t* addr); + Status physicalSelection(); + Status terminate(); + void reset(); + + void onLampStateChnaged(ILamp::ILampState state); + +private: + Initialization(const Initialization& other) = delete; + Initialization& operator=(const Initialization&) = delete; + + enum class Selection { + NONE, SELECTED, PHYSICAL_SELECTED + }; + + Status setSearchAddr(uint8_t addr, uint8_t offset); + + void operatingTimeStart(); + void operatingTimeStop(); + void checkOperatingTimeout(); + + ITimer* const mTimer; + Memory* const mMemoryController; + uint64_t mInitializeTime; + bool mInitialized; + bool mWithdraw; + Selection mSelected; +}; + +} // namespace controller +} // namespace dali + +#endif // DALI_CONFIGURATION_CONTROLER_H_ diff --git a/src/dali/controller/lamp.cpp b/src/dali/controller/lamp.cpp new file mode 100644 index 0000000..4b5bd3f --- /dev/null +++ b/src/dali/controller/lamp.cpp @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2015-2016, Arkadiusz Materek (arekmat@poczta.fm) + * + * Licensed under GNU General Public License 3.0 or later. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "lamp.hpp" + +#include "lamp_helper.hpp" + +#define DAPC_TIME_MS 200 + +namespace dali { +namespace controller { + +Lamp::Lamp(ILamp* lamp, Memory* memoryController) + : mLamp(lamp) + , mMemoryController(memoryController) + , mLampState(ILamp::ILampState::OK) + , mListener(nullptr) + , mIsPowerSet(false) + , mLimitError(false) + , mDapcTime(0) + , mConstPower(DALI_MASK) { + mLamp->registerClient(this); + setLevel(mMemoryController->getPhisicalMinLevel(), 0); +} + +Lamp::~Lamp() { + mLamp->unregisterClient(this); +} + +void Lamp::onReset() { + setLevel(mMemoryController->getMaxLevel(), 0); + mIsPowerSet = true; +} + +bool Lamp::isPowerOn() { + return (mLamp->getLevel() > 0); +} + +bool Lamp::isFailure() { + switch (mLampState) { + case ILamp::ILampState::DISCONNECTED: + case ILamp::ILampState::OVERHEAT: + return true; + default: + return false; + } +} + +bool Lamp::isFading() { + return mLamp->isFading(); +} + +bool Lamp::isLimitError() { + return mLimitError; +} + +bool Lamp::isPowerSet() { + return mIsPowerSet; +} + +uint8_t Lamp::getLevel() { + if (mLampState == ILamp::ILampState::OK) { + return driver2level(mLamp->getLevel(), mMemoryController->getMinLevel()); + } + return mMemoryController->getActualLevel(); +} + +Status Lamp::setLevel(uint8_t level, uint32_t fadeTime) { + mLimitError = false; + + Status status = Status::OK; + if (level != DALI_MASK) { + uint8_t minLevel = mMemoryController->getMinLevel(); + uint8_t maxLevel = mMemoryController->getMaxLevel(); + + if ((level != 0) && (level < minLevel)) { + mLimitError = true; + level = minLevel; + } + if (level > maxLevel) { + mLimitError = true; + level = maxLevel; + } + + mLamp->setLevel(level2driver(level), fadeTime); + status = mMemoryController->setActualLevel(level); + } + return status; +} + +uint32_t Lamp::getFadeTime() { + return kFadeTime[mMemoryController->getFadeTime()]; +} + +uint32_t Lamp::getFadeRate() { + return kFadeTime[mMemoryController->getFadeRate()]; +} + +Status Lamp::abortFading() { + if (mLamp->isFading()) { + mLamp->abortFading(); + uint8_t level = driver2level(mLamp->getLevel(), mMemoryController->getMinLevel()); + return mMemoryController->setActualLevel(level); + } + return Status::OK; +} + +void Lamp::setListener(Listener* listener) { + mListener = listener; +} + +void Lamp::notifyPowerDown() { + mLamp->setLevel(0, 0); +} + +Status Lamp::powerDirect(uint8_t level, uint64_t time) { + if (level == DALI_MASK) { + mLamp->abortFading(); + return Status::OK; + } + if (time - mDapcTime <= DAPC_TIME_MS) { + return dapcSequence(level, time); + } + onPowerCommand(); + return setLevel(level, getFadeTime()); +} + +Status Lamp::powerOff() { + onPowerCommand(); + return setLevel(0, 0); +} + +Status Lamp::powerScene(uint8_t scene) { + uint8_t level = mMemoryController->getLevelForScene(scene); + if (level == DALI_MASK) { + level = mMemoryController->getActualLevel(); + } + onPowerCommand(); + return setLevel(level, getFadeTime()); +} + +Status Lamp::powerUp() { + onPowerCommand(); + uint8_t fade = mMemoryController->getFadeRate(); + uint8_t steps = kStepsFor200FadeRate[fade]; + uint8_t level = getLevel(); + uint8_t maxLevel = mMemoryController->getMaxLevel(); + if ((int16_t) level + steps > (int16_t) maxLevel) { + level = maxLevel; + } else { + level += steps; + } + return setLevel(level, kFadeTime[fade]); +} + +Status Lamp::powerDown() { + onPowerCommand(); + uint8_t fade = mMemoryController->getFadeRate(); + uint8_t steps = kStepsFor200FadeRate[fade]; + uint8_t level = getLevel(); + uint8_t minLevel = mMemoryController->getMinLevel(); + if ((int16_t) level - steps < (int16_t) minLevel) { + level = minLevel; + } else { + level -= steps; + } + return setLevel(level, kFadeTime[fade]); +} + +Status Lamp::powerStepUp() { + onPowerCommand(); + uint8_t level = getLevel(); + uint8_t maxLevel = mMemoryController->getMaxLevel(); + if (level >= maxLevel) { + level = maxLevel; + } else { + level++; + } + return setLevel(level, 0); +} + +Status Lamp::powerStepDown() { + onPowerCommand(); + uint8_t level = getLevel(); + uint8_t minLevel = mMemoryController->getMinLevel(); + if (level <= minLevel) { + level = minLevel; + } else { + level--; + } + return setLevel(level, 0); +} + +Status Lamp::powerOnAndStepUp() { + onPowerCommand(); + uint8_t level = getLevel(); + if (level == 0) { + level = mMemoryController->getMinLevel(); + } else { + uint8_t maxLevel = mMemoryController->getMaxLevel(); + if (level >= maxLevel) { + level = maxLevel; + } else { + level++; + } + } + return setLevel(level, 0); +} + +Status Lamp::powerStepDownAndOff() { + onPowerCommand(); + uint8_t level = getLevel(); + uint8_t minLevel = mMemoryController->getMinLevel(); + if (level <= minLevel) { + level = 0; + } else { + level--; + } + return setLevel(level, 0); +} + +Status Lamp::powerRecallMinLevel() { + onPowerCommand(); + return setLevel(mMemoryController->getMinLevel(), 0); +} + +Status Lamp::powerRecallMaxLevel() { + onPowerCommand(); + return setLevel(mMemoryController->getMaxLevel(), 0); +} + +Status Lamp::powerRecallOnLevel() { + if (mIsPowerSet) { + return Status::INVALID; + } + uint8_t level = mMemoryController->getPowerOnLevel(); + if (level == DALI_MASK) { + level = mMemoryController->getActualLevel(); + } + return setLevel(level, 0); +} + +Status Lamp::powerRecallFaliureLevel() { + // TODO change mIsPowerSet if called before power on? + uint8_t level = mMemoryController->getFaliureLevel(); + if (level == DALI_MASK) { + level = mMemoryController->getActualLevel(); + } + return setLevel(level, 0); +} + +Status Lamp::enableDapcSequence(uint64_t time) { + mDapcTime = time; + return Status::OK; +} + +Status Lamp::dapcSequence(uint8_t level, uint64_t time) { + onPowerCommand(); + mDapcTime = time; + uint8_t actualLevel = getLevel(); + if (actualLevel == 0) { + // If the actual arc power level is zero, the new level shall be adopted without fading. + return setLevel(level, 0); + } + int16_t deltaLevel = (int32_t) level - actualLevel; + if (deltaLevel < 0) + deltaLevel = -deltaLevel; + if (deltaLevel == 0) + return Status::OK; + // deltaLevel should change in 200ms + int32_t fadeTime = (uint32_t) DALI_LEVEL_MAX * DAPC_TIME_MS / deltaLevel; + return setLevel(level, (uint32_t) fadeTime); +} + +void Lamp::onPowerCommand() { + mDapcTime = 0; + mIsPowerSet = true; +} + +void Lamp::onLampStateChnaged(ILamp::ILampState state) { + mLampState = state; + mListener->onLampStateChnaged(state); +} + +} // namespace controller +} // namespace dali diff --git a/src/dali/controller/lamp.hpp b/src/dali/controller/lamp.hpp new file mode 100644 index 0000000..acb8986 --- /dev/null +++ b/src/dali/controller/lamp.hpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2015-2016, Arkadiusz Materek (arekmat@poczta.fm) + * + * Licensed under GNU General Public License 3.0 or later. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef DALI_LAMP_CONTROLLER_HPP_ +#define DALI_LAMP_CONTROLLER_HPP_ + +#include + +#include "memory.hpp" + +namespace dali { +namespace controller { + +class Lamp: public ILamp::ILampClient { +public: + + class Listener { + public: + virtual void onLampStateChnaged(ILamp::ILampState state) = 0; + }; + + explicit Lamp(ILamp* lamp, Memory* memoryController); + virtual ~Lamp(); + +// >>> used only in controller namespace + void onReset(); + bool isPowerOn(); + bool isFailure(); + bool isFading(); + bool isLimitError(); + bool isPowerSet(); + + uint8_t getLevel(); + Status setLevel(uint8_t level, uint32_t fadeTime); + uint32_t getFadeTime(); + uint32_t getFadeRate(); + + Status abortFading(); +// <<< used only in controller namespace + + void setListener(Listener* listener); + void notifyPowerDown(); + + Status powerDirect(uint8_t level, uint64_t time); + Status powerOff(); + Status powerScene(uint8_t scene); + Status powerUp(); + Status powerDown(); + Status powerStepUp(); + Status powerStepDown(); + Status powerOnAndStepUp(); + Status powerStepDownAndOff(); + Status powerRecallMinLevel(); + Status powerRecallMaxLevel(); + Status powerRecallOnLevel(); + Status powerRecallFaliureLevel(); + Status enableDapcSequence(uint64_t time); + +private: + Lamp(const Lamp& other) = delete; + Lamp& operator=(const Lamp&) = delete; + + Status dapcSequence(uint8_t level, uint64_t time); + + void onPowerCommand(); + + // ILamp::ILampClient + void onLampStateChnaged(ILamp::ILampState state) override; + + ILamp* const mLamp; + Memory* const mMemoryController; + ILamp::ILampState mLampState; + Listener* mListener; + bool mIsPowerSet; + bool mLimitError; + uint64_t mDapcTime; + uint8_t mConstPower; +}; + +} // namespace controller +} // namespace dali + +#endif // DALI_LAMP_CONTROLLER_HPP_ diff --git a/src/dali/controller/lamp_helper.cpp b/src/dali/controller/lamp_helper.cpp new file mode 100644 index 0000000..c7e5e3b --- /dev/null +++ b/src/dali/controller/lamp_helper.cpp @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2015-2016, Arkadiusz Materek (arekmat@poczta.fm) + * + * Licensed under GNU General Public License 3.0 or later. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "lamp_helper.hpp" + +#define DAPC_TIME_MS 200 + +namespace dali { +namespace controller { +namespace { + +const uint16_t kLevel2driver[256] = { + 0, + 66, + 67, + 69, + 71, + 73, + 75, + 77, + 79, + 82, + 84, + 86, + 88, + 91, + 93, + 96, + 99, + 101, + 104, + 107, + 110, + 113, + 116, + 119, + 123, + 126, + 130, + 133, + 137, + 141, + 145, + 149, + 153, + 157, + 161, + 166, + 170, + 175, + 180, + 185, + 190, + 195, + 201, + 206, + 212, + 218, + 224, + 230, + 236, + 243, + 250, + 257, + 264, + 271, + 279, + 286, + 294, + 302, + 311, + 319, + 328, + 337, + 347, + 356, + 366, + 376, + 387, + 397, + 408, + 420, + 431, + 443, + 455, + 468, + 481, + 494, + 508, + 522, + 536, + 551, + 567, + 582, + 598, + 615, + 632, + 649, + 667, + 686, + 705, + 724, + 744, + 765, + 786, + 808, + 830, + 853, + 877, + 901, + 926, + 952, + 978, + 1005, + 1033, + 1062, + 1091, + 1121, + 1152, + 1184, + 1217, + 1251, + 1285, + 1321, + 1357, + 1395, + 1434, + 1473, + 1514, + 1556, + 1599, + 1643, + 1689, + 1735, + 1783, + 1833, + 1884, + 1936, + 1989, + 2044, + 2101, + 2159, + 2219, + 2280, + 2343, + 2408, + 2475, + 2543, + 2614, + 2686, + 2760, + 2837, + 2915, + 2996, + 3079, + 3164, + 3252, + 3342, + 3434, + 3529, + 3627, + 3728, + 3831, + 3937, + 4046, + 4158, + 4273, + 4391, + 4513, + 4637, + 4766, + 4898, + 5033, + 5173, + 5316, + 5463, + 5614, + 5770, + 5929, + 6093, + 6262, + 6435, + 6614, + 6797, + 6985, + 7178, + 7377, + 7581, + 7791, + 8006, + 8228, + 8456, + 8690, + 8930, + 9178, + 9432, + 9693, + 9961, + 10237, + 10520, + 10811, + 11110, + 11418, + 11734, + 12059, + 12393, + 12736, + 13088, + 13450, + 13823, + 14205, + 14598, + 15003, + 15418, + 15845, + 16283, + 16734, + 17197, + 17673, + 18162, + 18665, + 19182, + 19712, + 20258, + 20819, + 21395, + 21987, + 22596, + 23221, + 23864, + 24525, + 25203, + 25901, + 26618, + 27355, + 28112, + 28890, + 29690, + 30512, + 31356, + 32224, + 33116, + 34033, + 34975, + 35943, + 36938, + 37960, + 39011, + 40090, + 41200, + 42341, + 43513, + 44717, + 45955, + 47227, + 48534, + 49877, + 51258, + 52677, + 54135, + 55633, + 57173, + 58756, + 60382, + 62053, + 63771, + 65535, + 0, +}; +} // namespace + +const uint32_t kFadeTime[16] = { // fade times from 0 to 254 in milliseconds + 0, // 0 + 707, // 1 + 1000, // 2 + 1414, // 3 + 2000, // 4 + 2828, // 5 + 4000, // 6 + 5657, // 7 + 8000, // 8 + 11314, // 9 + 16000, // 10 + 22627, // 11 + 32000, // 12 + 45255, // 13 + 64000, // 14 + 90510, // 15 +}; + +const uint8_t kStepsFor200FadeRate[16] = { 1, 72, 51, 36, 25, 18, 13, 9, 6, 4, 3, 2, 2, 1, 1, 1 }; + +uint16_t level2driver(uint8_t level) { + return kLevel2driver[level]; +} + +uint8_t driver2level(uint16_t driverLevel, uint8_t minLevel) { + if (driverLevel < kLevel2driver[minLevel]) { + return 0; + } + uint16_t a = minLevel; + uint16_t b = DALI_LEVEL_MAX; + while (true) { + uint16_t m = (a + b) / 2; + uint16_t v = kLevel2driver[m]; + if (driverLevel == v) { + return m; + } else if (driverLevel < v) { + b = m; + if (b - a <= 1) { + uint16_t av = driverLevel - kLevel2driver[a]; + uint16_t bv = v - driverLevel; + if (av < bv) { + return a; + } else { + return b; + } + } + } else { + a = m; + if (b - a <= 1) { + uint16_t av = driverLevel - v; + uint16_t bv = kLevel2driver[b] - driverLevel; + if (av < bv) { + return a; + } else { + return b; + } + } + } + } +} +} // namespace controller +} // namespace dali diff --git a/src/dali/controller/lamp_helper.hpp b/src/dali/controller/lamp_helper.hpp new file mode 100644 index 0000000..2bbbe68 --- /dev/null +++ b/src/dali/controller/lamp_helper.hpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2015-2016, Arkadiusz Materek (arekmat@poczta.fm) + * + * Licensed under GNU General Public License 3.0 or later. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef DALI_LAMP_CONTROLLER_CONSTS_HPP_ +#define DALI_LAMP_CONTROLLER_CONSTS_HPP_ + +#include + +namespace dali { +namespace controller { + +uint16_t level2driver(uint8_t level); +uint8_t driver2level(uint16_t driverLevel, uint8_t minLevel); + +extern const uint32_t kFadeTime[16]; +extern const uint8_t kStepsFor200FadeRate[16]; + +} // namespace controller +} // namespace dali + +#endif // DALI_LAMP_CONTROLLER_CONSTS_HPP_ diff --git a/src/dali/controller/memory.cpp b/src/dali/controller/memory.cpp new file mode 100644 index 0000000..e5e1cba --- /dev/null +++ b/src/dali/controller/memory.cpp @@ -0,0 +1,429 @@ +/* + * Copyright (c) 2015-2016, Arkadiusz Materek (arekmat@poczta.fm) + * + * Licensed under GNU General Public License 3.0 or later. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "memory.hpp" + +#include + +#define DATA_FIELD_OFFSET(type, field) ((uintptr_t) &(((type *) 0)->field)) +#define TEMP_FIELD_OFFSET(type, field) ((uintptr_t) &(((type *) 0)->field)) + +#define LONG_ADDR_MASK 0x00ffffff + +#define INVALID_BANK_ADDR 0xffffffff + +namespace dali { +namespace controller { + +Memory::Memory(IMemory* memory) : + mMemory(memory), + mData((Data*) memory->data(DALI_BANK2_ADDR, sizeof(Data))), + mTemp((Temp*) memory->tempData(0, sizeof(Temp))) +{ + resetRam(true); + + for (uint8_t i = 0; i < DALI_BANKS; ++i) { + mBankData[i] = mMemory->data(getBankAddr(i), getBankSize(i)); + if ((uintptr_t) mBankData[i] == INVALID_BANK_ADDR) { + mBankData[i] = nullptr; + } + resetBankIfNeeded(i); + } + + if (mData != nullptr) { + if (!isDataValid()) { + resetData(true); + } + } + if (mTemp != nullptr) { + if (!isTempValid()) { + resetTemp(); + } + } + setSearchAddr(LONG_ADDR_MASK); +} + +Status Memory::readMemory(uint8_t* data) { + uint8_t bank = mRam.dtr1; + uint8_t addr = mRam.dtr; + + Status status = Status::OK; + if (bankRead(bank, addr++, data) != Status::OK) { + status = Status::ERROR; + } + if (bankRead(bank, addr, &mRam.dtr2) != Status::OK) { + // ignore status + } + if (status == Status::OK) { + mRam.dtr++; + } + return status; +} + +Status Memory::writeMemory(uint8_t data) { + uint8_t bank = mRam.dtr1; + uint8_t addr = mRam.dtr; + + Status status = bankWrite(bank, addr, data, false); + if (status == Status::OK) { + mRam.dtr++; + } + return status; +} + +uint8_t Memory::getPhisicalMinLevel() { + return mData->phisicalMinLevel; +} + +Status Memory::setPhisicalMinLevel(uint8_t level) { + return writeData8(DATA_FIELD_OFFSET(Data, phisicalMinLevel), level); +} + +uint8_t Memory::getShortAddr() { + return mData->shortAddr; +} + +Status Memory::setShortAddr(uint8_t addr) { + addr |= 0x01; // normalize address + return writeData8(DATA_FIELD_OFFSET(Data, shortAddr), addr); +} + +uint8_t Memory::getMinLevel() { + return mData->minLevel; +} + +Status Memory::setMinLevel(uint8_t level) { + return writeData8(DATA_FIELD_OFFSET(Data, minLevel), level); +} + +uint8_t Memory::getMaxLevel() { + return mData->maxLevel; +} + +Status Memory::setMaxLevel(uint8_t level) { + return writeData8(DATA_FIELD_OFFSET(Data, maxLevel), level); +} + +uint8_t Memory::getPowerOnLevel() { + return mData->powerOnLevel; +} + +Status Memory::setPowerOnLevel(uint8_t level) { + return writeData8(DATA_FIELD_OFFSET(Data, powerOnLevel), level); +} + +uint8_t Memory::getFaliureLevel() { + return mData->failureLevel; +} + +Status Memory::setFaliureLevel(uint8_t level) { + return writeData8(DATA_FIELD_OFFSET(Data, failureLevel), level); +} + +uint8_t Memory::getFadeTime() { + return mData->fadeTime; +} + +Status Memory::setFadeTime(uint8_t fadeTime) { + return writeData8(DATA_FIELD_OFFSET(Data, fadeTime), fadeTime); +} + +uint8_t Memory::getFadeRate() { + return mData->fadeRate; +} + +Status Memory::setFadeRate(uint8_t fadeRate) { + return writeData8(DATA_FIELD_OFFSET(Data, fadeRate), fadeRate); +} + +uint8_t Memory::getLevelForScene(uint8_t scene) { + if (scene > DALI_SCENE_MAX) { + return DALI_MASK; + } + return mData->scene[scene]; +} + +Status Memory::setLevelForScene(uint8_t scene, uint8_t level) { + if (scene > DALI_SCENE_MAX) { + return Status::ERROR; + } + return writeData8(DATA_FIELD_OFFSET(Data, scene[scene]), level); +} + +uint16_t Memory::getGroups() { + return mData->groups; +} + +uint8_t Memory::getGroupsL() { + return mData->groups; +} + +uint8_t Memory::getGroupsH() { + return mData->groups >> 8; +} + +Status Memory::setGroups(uint16_t groups) { + return writeData16(DATA_FIELD_OFFSET(Data, groups), groups); +} + + +uint32_t Memory::getSearchAddr() { + return mRam.searchAddr; +} + +Status Memory::setSearchAddr(uint32_t searchAddr) { + mRam.searchAddr = searchAddr & LONG_ADDR_MASK; + return Status::OK; +} + +uint32_t Memory::getRandomAddr() { + return mTemp->randomAddr; +} + +Status Memory::setRandomAddr(uint32_t randomAddr) { + randomAddr &= LONG_ADDR_MASK; + return writeTemp32(TEMP_FIELD_OFFSET(Temp, randomAddr), randomAddr); +} + +uint8_t Memory::getActualLevel() { + return mTemp->actualLevel; +} + +Status Memory::setActualLevel(uint8_t level) { + return writeTemp8(TEMP_FIELD_OFFSET(Temp, actualLevel), level); +} + +bool Memory::isDataValid() { + // check ranges of values + if ((mData->phisicalMinLevel == 0) || (mData->phisicalMinLevel == DALI_MASK)) + return false; + if (mData->fadeRate < DALI_FADE_RATE_MIN || mData->fadeRate > DALI_FADE_RATE_MAX) + return false; + if (mData->fadeTime < DALI_FADE_TIME_MIN || mData->fadeTime > DALI_FADE_TIME_MAX) + return false; + if (mData->shortAddr != DALI_MASK && (mData->shortAddr >> 1) > DALI_ADDR_MAX) + return false; + if (mData->minLevel > mData->maxLevel) + return false; + return true; +} + +bool Memory::isTempValid() { + if (mTemp->randomAddr > LONG_ADDR_MASK) + return false; + return true; +} + +bool Memory::isReset() { + if (mData->powerOnLevel != DALI_LEVEL_MAX) + return false; + if (mData->failureLevel != DALI_LEVEL_MAX) + return false; + if (mData->minLevel != getPhisicalMinLevel()) + return false; + if (mData->maxLevel != DALI_LEVEL_MAX) + return false; + if (mData->fadeRate != DALI_FADE_RATE_DEFAULT) + return false; + + if (mData->fadeTime != DALI_FADE_TIME_DEFAULT) + return false; + // skip checking mData->shortAddr + if (mData->groups != 0) + return false; + for (uint16_t i = 0; i <= DALI_SCENE_MAX; i++) { + if (mData->scene[i] != DALI_MASK) + return false; + } + if (mTemp->randomAddr != LONG_ADDR_MASK) + return false; + return true; +} + +Status Memory::reset() { + resetRam(false); + resetData(false); + resetTemp(); + return Status::OK; +} + +void Memory::resetRam(bool initialize) { + if (initialize) { + mRam.dtr = DALI_MASK; + mRam.dtr1 = DALI_MASK; + mRam.dtr2 = DALI_MASK; + } + mRam.searchAddr = LONG_ADDR_MASK; +} + +void Memory::resetData(bool initialize) { + if (initialize) { + setPhisicalMinLevel(DALI_PHISICAL_MIN_LEVEL); + } + setPowerOnLevel(DALI_LEVEL_MAX); + setFaliureLevel(DALI_LEVEL_MAX); + setMinLevel(getPhisicalMinLevel()); + setMaxLevel(DALI_LEVEL_MAX); + setFadeRate(DALI_FADE_RATE_DEFAULT); + setFadeTime(DALI_FADE_TIME_DEFAULT); + if (initialize) { + setShortAddr(DALI_MASK); + } + setGroups(0); + for (size_t i = 0; i < 16; i++) { + setLevelForScene(i, DALI_MASK); + } +} + +void Memory::resetTemp() { + setRandomAddr(LONG_ADDR_MASK); + setActualLevel(DALI_MASK); +} + +Status Memory::internalBankWrite(uint8_t bank, uint8_t addr, uint8_t* data, uint8_t size) { + Status status = Status::OK; + const uint8_t* bankData = mBankData[bank]; + uint8_t crc = bankData[1]; + + const uintptr_t bankAddr = getBankAddr(bank); + const uint16_t endAddr = (uint16_t)addr + size; + for (; addr < endAddr; ++addr, ++data) { + crc += bankData[addr]; + crc -= *data; + + if (mMemory->dataWrite(bankAddr + addr, data, 1) != 1) { + status = Status::ERROR; + } + } + + if (mMemory->dataWrite(bankAddr + 1, &crc, 1) != 1) { + status = Status::ERROR; + } + + return status; +} + +size_t Memory::getBankSize(uint8_t bank) { + switch (bank) { + case 0: + return DALI_BANK1_ADDR - DALI_BANK0_ADDR; + case 1: + return DALI_BANK2_ADDR - DALI_BANK1_ADDR; + case 2: + return DALI_BANK3_ADDR - DALI_BANK2_ADDR; + default: + return 0; + } +} + +uintptr_t Memory::getBankAddr(uint8_t bank) { + switch (bank) { + case 0: // read only + return DALI_BANK0_ADDR; + case 1: + return DALI_BANK1_ADDR; + case 2: + return DALI_BANK2_ADDR; + default: + return INVALID_BANK_ADDR; + } +} + +Status Memory::bankWrite(uint8_t bank, uint8_t addr, uint8_t data, bool force) { + uint8_t size = getBankSize(bank); + if (size < 3 && addr > size) { + return Status::ERROR; + } + if (!force && !isBankAddrWritable(bank, addr)) { + return Status::INVALID; + } + return internalBankWrite(bank, addr, &data, sizeof(uint8_t)); +} + +Status Memory::bankRead(uint8_t bank, uint8_t addr, uint8_t* data) { + uint8_t size = getBankSize(bank); + if (addr + 1 > size) { + return Status::ERROR; + } + uintptr_t bankAddr = getBankAddr(bank); + if (bankAddr == INVALID_BANK_ADDR) { + return Status::ERROR; + } + const uint8_t* bankData = mMemory->data(bankAddr + addr, 1); + if (bankData == nullptr) { + return dali::Status::ERROR; + } + *data = *bankData; + return Status::OK; +} + +bool Memory::isBankAddrWritable(uint8_t bank, uint8_t addr) { + if (bank == 0) { + // read only + return false; + } + + uintptr_t bankAddr = getBankAddr(bank); + if (bankAddr != INVALID_BANK_ADDR) { + if (addr < 2) { + return false; + } + if (addr == 2) { + return true; + } + const uint8_t* bankData = mMemory->data(bankAddr + 2, 1); + if ((bankData == nullptr) || (*bankData != 0x55)) { + return false; + } + return true; + } + return false; +} + +void Memory::resetBankIfNeeded(uint8_t bank) { + if ((bank >= DALI_BANKS) || (mBankData[bank] == nullptr)) { + return; + } + const size_t bankSize = getBankSize(bank); + + bool reset = false; + const uint8_t* bankData = mBankData[bank]; + if (bankData[0] != (uint8_t) (bankSize - 1)) { + reset = true; + } + if (!reset) { + uint8_t checksum = 0; + ++bankData; + for (uint8_t i = 1; i < bankSize; ++i, ++bankData) { + checksum += *bankData; + } + reset = (checksum != 0); + } + + if (reset) { + uintptr_t bankAddr = getBankAddr(bank); + uint8_t temp = bankSize - 1; // size + mMemory->dataWrite(bankAddr, &temp, 1); + temp = 0 - (0xff * (bankSize - 2)); // crc + mMemory->dataWrite(bankAddr + 1, &temp, 1); + temp = 0xff; // reset data + for (uint8_t i = 2; i < bankSize; ++i) { + mMemory->dataWrite(bankAddr + i, &temp, 1); + } + + if (bank == 0) { + uint8_t banks = DALI_BANKS - 1; + internalBankWrite(0, 0x02, &banks, 1); + } + } +} + +} // namespace controller +} // namespace dali diff --git a/src/dali/controller/memory.hpp b/src/dali/controller/memory.hpp new file mode 100644 index 0000000..80db93f --- /dev/null +++ b/src/dali/controller/memory.hpp @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2015-2016, Arkadiusz Materek (arekmat@poczta.fm) + * + * Licensed under GNU General Public License 3.0 or later. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef DALI_MEMORY_CONTROLLER_HPP_ +#define DALI_MEMORY_CONTROLLER_HPP_ + +#include + +namespace dali { +namespace controller { + +class Memory { +public: + explicit Memory(IMemory* memory); + virtual ~Memory() {}; + + uint8_t getDTR() { + return mRam.dtr; + } + + void setDTR(uint8_t value) { + mRam.dtr = value; + } + + uint8_t getDTR1() { + return mRam.dtr1; + } + + void setDTR1(uint8_t value) { + mRam.dtr1 = value; + } + + uint8_t getDTR2() { + return mRam.dtr2; + } + + void setDTR2(uint8_t value) { + mRam.dtr2 = value; + } + + Status readMemory(uint8_t* data); + Status writeMemory(uint8_t data); + + uint8_t getPhisicalMinLevel(); + Status setPhisicalMinLevel(uint8_t level); + + uint8_t getShortAddr(); + Status setShortAddr(uint8_t addr); + + uint8_t getMinLevel(); + Status setMinLevel(uint8_t level); + + uint8_t getMaxLevel(); + Status setMaxLevel(uint8_t level); + + uint8_t getPowerOnLevel(); + Status setPowerOnLevel(uint8_t level); + + uint8_t getFaliureLevel(); + Status setFaliureLevel(uint8_t level); + + uint8_t getFadeTime(); + Status setFadeTime(uint8_t fadeTime); + + uint8_t getFadeRate(); + Status setFadeRate(uint8_t fadeRate); + + uint8_t getLevelForScene(uint8_t scene); + Status setLevelForScene(uint8_t scene, uint8_t level); + + uint16_t getGroups(); + uint8_t getGroupsL(); + uint8_t getGroupsH(); + Status setGroups(uint16_t groups); + + uint32_t getSearchAddr(); + Status setSearchAddr(uint32_t searchAddr); + + uint32_t getRandomAddr(); + Status setRandomAddr(uint32_t randomAddr); + + uint8_t getActualLevel(); + Status setActualLevel(uint8_t level); + + bool isValid() { + return isDataValid() && isTempValid(); + } + + bool isReset(); + Status reset(); + + uint16_t uint16FromDtrAndDtr1() { + return ((uint16_t) mRam.dtr1 << 8) | mRam.dtr; + } + +private: + Memory(const Memory& other) = delete; + Memory& operator=(const Memory&) = delete; + + typedef struct __attribute__((__packed__)) { + uint32_t randomAddr; + uint8_t actualLevel; + uint8_t reversed1; + uint8_t reversed2; + uint8_t reversed3; + } Temp; + + typedef struct __attribute__((__packed__)) { + uint8_t size; // BANK mandatory field + uint8_t crc; // BANK mandatory field + uint8_t phisicalMinLevel; + uint8_t powerOnLevel; + uint8_t failureLevel; + uint8_t minLevel; + uint8_t maxLevel; + uint8_t fadeRate; + uint8_t fadeTime; + uint8_t shortAddr; + uint16_t groups; + uint8_t scene[16]; + } Data; + + typedef struct { + uint8_t dtr; + uint8_t dtr1; + uint8_t dtr2; + uint32_t searchAddr; + } Ram; + + Status internalBankWrite(uint8_t bank, uint8_t addr, uint8_t* data, uint8_t size); + + Status writeTemp(uintptr_t addr, uint8_t* data, size_t size) { + return mMemory->tempWrite(addr, data, size) == size ? Status::OK : Status::ERROR; + } + + Status writeTemp8(uintptr_t addr, uint8_t data) { + return mMemory->tempWrite(addr, &data, sizeof(uint8_t)) == sizeof(uint8_t) ? Status::OK : Status::ERROR; + } + + Status writeTemp16(uintptr_t addr, uint16_t data) { + return mMemory->tempWrite(addr, (uint8_t*) &data, sizeof(uint16_t)) == sizeof(uint16_t) ? Status::OK : Status::ERROR; + } + + Status writeTemp32(uintptr_t addr, uint32_t data) { + return mMemory->tempWrite(addr, (uint8_t*) &data, sizeof(uint32_t)) == sizeof(uint32_t) ? Status::OK : Status::ERROR; + } + + Status writeData8(uintptr_t addr, uint8_t data) { + return internalBankWrite(2, addr, &data, sizeof(uint8_t)); + } + + Status writeData16(uintptr_t addr, uint16_t data) { + return internalBankWrite(2, addr, (uint8_t*)&data, sizeof(uint16_t)); + } + + Status writeData32(uintptr_t addr, uint32_t data) { + return internalBankWrite(2, addr, (uint8_t*)&data, sizeof(uint16_t)); + } + + Status writeData(uintptr_t addr, uint8_t* data, size_t size) { + return internalBankWrite(2, addr, data, size); + } + + Status bankWrite(uint8_t bank, uint8_t addr, uint8_t data, bool force); + Status bankRead(uint8_t bank, uint8_t addr, uint8_t* data); + + + bool isDataValid(); + bool isTempValid(); + void resetRam(bool initialize); + void resetData(bool initialize); + void resetTemp(); + + size_t getBankSize(uint8_t bank); + uintptr_t getBankAddr(uint8_t bank); + bool isBankAddrWritable(uint8_t bank, uint8_t addr); + void resetBankIfNeeded(uint8_t bank); + + IMemory* const mMemory; + Ram mRam; + const uint8_t* mBankData[DALI_BANKS]; + const Data* mData; + const Temp* mTemp; +}; + +} // namespace controller +} // namespace dali + +#endif // DALI_MEMORY_CONTROLLER_HPP_ diff --git a/src/dali/controller/query_store.cpp b/src/dali/controller/query_store.cpp new file mode 100644 index 0000000..f5c1552 --- /dev/null +++ b/src/dali/controller/query_store.cpp @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2015-2016, Arkadiusz Materek (arekmat@poczta.fm) + * + * Licensed under GNU General Public License 3.0 or later. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "query_store.hpp" + +namespace dali { +namespace controller { + +QueryStore::QueryStore(Memory* memory, Lamp* lamp) : + mMemoryController(memory), mLampController(lamp) { +} + +Status QueryStore::reset() { + Status status = mMemoryController->reset(); + mLampController->onReset(); + return status; +} + +Status QueryStore::storeActualLevelInDtr() { + mMemoryController->setDTR(mLampController->getLevel()); + return Status::OK; +} + +Status QueryStore::storeDtrAsMaxLevel() { + uint8_t minLevel = mMemoryController->getMinLevel(); + uint8_t maxLevel = mMemoryController->getDTR(); + if (maxLevel < minLevel) { + maxLevel = minLevel; + } + if (maxLevel > DALI_LEVEL_MAX) { + maxLevel = DALI_LEVEL_MAX; + } + Status status = mMemoryController->setMaxLevel(maxLevel); + + uint8_t level = mMemoryController->getActualLevel(); + if ((level != 0) && (level > maxLevel)) { + mLampController->setLevel(maxLevel, 0); + } + return status; +} + +Status QueryStore::storeDtrAsMinLevel() { + uint8_t minLevel = mMemoryController->getDTR(); + uint8_t maxLevel = mMemoryController->getMaxLevel(); + if (minLevel > maxLevel) { + minLevel = maxLevel; + } + uint8_t phiscalMinLevel = mMemoryController->getPhisicalMinLevel(); + if (minLevel < phiscalMinLevel) { + minLevel = phiscalMinLevel; + } + Status status = mMemoryController->setMinLevel(minLevel); + + uint8_t level = mMemoryController->getActualLevel(); + if ((level != 0) && (level < minLevel)) { + mLampController->setLevel(minLevel, 0); + } + return status; +} + +Status QueryStore::storeDtrAsFailureLevel() { + return mMemoryController->setFaliureLevel(mMemoryController->getDTR()); +} + +Status QueryStore::storePowerOnLevel() { + return mMemoryController->setPowerOnLevel(mMemoryController->getDTR()); +} + +Status QueryStore::storeDtrAsFadeTime() { + uint8_t fadeTime = mMemoryController->getDTR(); + if (fadeTime < DALI_FADE_TIME_MIN) { + fadeTime = DALI_FADE_TIME_MIN; + } + if (fadeTime > DALI_FADE_TIME_MAX) { + fadeTime = DALI_FADE_TIME_MAX; + } + return mMemoryController->setFadeTime(fadeTime); +} + +Status QueryStore::storeDtrAsFadeRate() { + uint8_t fadeRate = mMemoryController->getDTR(); + if (fadeRate < DALI_FADE_RATE_MIN) { + fadeRate = DALI_FADE_RATE_MIN; + } + if (fadeRate > DALI_FADE_RATE_MAX) { + fadeRate = DALI_FADE_RATE_MAX; + } + return mMemoryController->setFadeRate(fadeRate); +} + +Status QueryStore::storeDtrAsScene(uint8_t scene) { + return mMemoryController->setLevelForScene(scene, mMemoryController->getDTR()); +} + +Status QueryStore::removeFromScene(uint8_t scene) { + return mMemoryController->setLevelForScene(scene, DALI_MASK); +} + +Status QueryStore::addToGroup(uint8_t group) { + if (group > DALI_GROUP_MAX) { + return Status::ERROR; + } + uint16_t groups = mMemoryController->getGroups(); + groups |= 1 << group; + return mMemoryController->setGroups(groups); +} + +Status QueryStore::removeFromGroup(uint8_t group) { + if (group > DALI_GROUP_MAX) { + return Status::ERROR; + } + uint16_t groups = mMemoryController->getGroups(); + groups &= ~(1 << group); + return mMemoryController->setGroups(groups); +} + +Status QueryStore::storeDtrAsShortAddr() { + uint8_t addr = mMemoryController->getDTR(); + if ((addr != DALI_MASK) && ((addr >> 1) > DALI_ADDR_MAX)) { + return Status::ERROR; + } + return mMemoryController->setShortAddr(addr); +} + +uint8_t QueryStore::queryStatus() { + uint8_t status = 0; + + if (!isMemoryValid()) { // FIXME this should be false but for debug purpose is checked + status |= (1 << 0); + } + if (queryLampFailure()) { + status |= (1 << 1); + } + if (queryLampPowerOn()) { + status |= (1 << 2); + } + if (queryLampLimitError()) { + status |= (1 << 3); + } + if (queryIsFading()) { + status |= (1 << 4); + } + if (queryResetState()) { + status |= (1 << 5); + } + if (queryMissingShortAddr()) { + status |= (1 << 6); + } + if (!queryLampPowerSet()) { + status |= (1 << 7); + } + return status; +} + +bool QueryStore::queryLampFailure() { + return mLampController->isFailure(); +} + +bool QueryStore::queryLampPowerOn() { + return mLampController->isPowerOn(); +} + +bool QueryStore::queryLampLimitError() { + return mLampController->isLimitError(); +} + +bool QueryStore::queryIsFading() { + return mLampController->isFading(); +} + +bool QueryStore::queryResetState() { + return mMemoryController->isReset(); +} + +bool QueryStore::queryMissingShortAddr() { + return mMemoryController->getShortAddr() == DALI_MASK; +} + +bool QueryStore::queryLampPowerSet() { + return mLampController->isPowerSet(); +} + +uint8_t QueryStore::queryActualLevel() { + return mLampController->getLevel(); +} + +uint8_t QueryStore::queryMaxLevel() { + return mMemoryController->getMaxLevel(); +} + +uint8_t QueryStore::queryMinLevel() { + return mMemoryController->getMinLevel(); +} + +uint8_t QueryStore::queryPowerOnLevel() { + return mMemoryController->getPowerOnLevel(); +} + +uint8_t QueryStore::queryFaliureLevel() { + return mMemoryController->getFaliureLevel(); +} + +uint8_t QueryStore::queryFadeRateOrTime() { + uint8_t fadeRate = mMemoryController->getFadeRate(); + uint8_t fadeTime = mMemoryController->getFadeTime(); + return (fadeTime << 4) | fadeRate; +} + +uint8_t QueryStore::queryLevelForScene(uint8_t scene) { + return mMemoryController->getLevelForScene(scene); +} + +uint8_t QueryStore::queryGroupsL() { + return mMemoryController->getGroupsL(); +} + +uint8_t QueryStore::queryGroupsH() { + return mMemoryController->getGroupsH(); +} + +uint8_t QueryStore::queryRandomAddrH() { + return (uint8_t) ((mMemoryController->getRandomAddr() >> 16) & 0xff); +} + +uint8_t QueryStore::queryRandomAddrM() { + return (uint8_t) ((mMemoryController->getRandomAddr() >> 8) & 0xff); +} + +uint8_t QueryStore::queryRandomAddrL() { + return (uint8_t) ((mMemoryController->getRandomAddr() >> 0) & 0xff); +} + +bool QueryStore::isMemoryValid() { + return mMemoryController->isValid(); +} + +} // namespace controller +} // namespace dali diff --git a/src/dali/controller/query_store.hpp b/src/dali/controller/query_store.hpp new file mode 100644 index 0000000..4c9d680 --- /dev/null +++ b/src/dali/controller/query_store.hpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2015-2016, Arkadiusz Materek (arekmat@poczta.fm) + * + * Licensed under GNU General Public License 3.0 or later. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef DALI_CONTROLLER_QUERY_HPP_ +#define DALI_CONTROLLER_QUERY_HPP_ + +#include "lamp.hpp" +#include "memory.hpp" + +namespace dali { +namespace controller { + +class QueryStore { + +public: + explicit QueryStore(Memory* memory, Lamp* lamp); + virtual ~QueryStore() {}; + + Status reset(); + Status storeActualLevelInDtr(); + Status storeDtrAsMaxLevel(); + Status storeDtrAsMinLevel(); + Status storeDtrAsFailureLevel(); + Status storePowerOnLevel(); + Status storeDtrAsFadeTime(); + Status storeDtrAsFadeRate(); + Status storeDtrAsScene(uint8_t scene); + Status removeFromScene(uint8_t scene); + Status addToGroup(uint8_t group); + Status removeFromGroup(uint8_t group); + Status storeDtrAsShortAddr(); + uint8_t queryStatus(); + bool queryLampFailure(); + bool queryLampPowerOn(); + bool queryLampLimitError(); + bool queryIsFading(); + bool queryResetState(); + bool queryMissingShortAddr(); + bool queryLampPowerSet(); + uint8_t queryActualLevel(); + uint8_t queryMaxLevel(); + uint8_t queryMinLevel(); + uint8_t queryPowerOnLevel(); + uint8_t queryFaliureLevel(); + uint8_t queryFadeRateOrTime(); + uint8_t queryLevelForScene(uint8_t scene); + uint8_t queryGroupsL(); + uint8_t queryGroupsH(); + uint8_t queryRandomAddrH(); + uint8_t queryRandomAddrM(); + uint8_t queryRandomAddrL(); + +private: + bool isMemoryValid(); + + QueryStore(const QueryStore& other) = delete; + QueryStore& operator=(const QueryStore&) = delete; + + Memory* const mMemoryController; + Lamp* const mLampController; +}; + +} // namespace controller +} // namespace dali + +#endif // DALI_CONTROLLER_QUERY_HPP_ diff --git a/src/dali/dali.hpp b/src/dali/dali.hpp new file mode 100644 index 0000000..240d023 --- /dev/null +++ b/src/dali/dali.hpp @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2015-2016, Arkadiusz Materek (arekmat@poczta.fm) + * + * Licensed under GNU General Public License 3.0 or later. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef DALI_DALI_HPP_ +#define DALI_DALI_HPP_ + +#include "config.hpp" +#include "commands.hpp" + +#include +#include +#include +#include + +#define DALI_MASK 255 + +#define DALI_LEVEL_MAX 254 +#define DALI_LEVEL_DEFAULT 254 + +#define DALI_FADE_TIME_MIN 0 +#define DALI_FADE_TIME_MAX 15 +#define DALI_FADE_TIME_DEFAULT 0 + +#define DALI_FADE_RATE_MIN 1 +#define DALI_FADE_RATE_MAX 15 +#define DALI_FADE_RATE_DEFAULT 7 + +#define DALI_SCENE_MAX 15 +#define DALI_GROUP_MAX 15 +#define DALI_ADDR_MAX 63 +#define DALI_ACK_YES DALI_MASK + +namespace dali { + +enum class Status { + OK, ERROR, INVALID, INVALID_STATE, REPEAT_REQUIRED +}; + +class IMemory { +public: + class IMemoryClient { + public: + virtual void onBankReset(uint8_t bank) = 0; + }; + + virtual size_t dataSize() = 0; + virtual size_t dataWrite(uintptr_t addr, const uint8_t* data, size_t size) = 0; + virtual const uint8_t* data(uintptr_t addr, size_t size) = 0; + + virtual size_t tempSize() = 0; + virtual size_t tempWrite(uintptr_t addr, const uint8_t* data, size_t size) = 0; + virtual const uint8_t* tempData(uintptr_t addr, size_t size) = 0; +}; + +class ILamp { +public: + enum class ILampState { + OK, DISCONNECTED, OVERHEAT + }; + + class ILampClient { + public: + virtual void onLampStateChnaged(ILampState state) = 0; + }; + + virtual Status registerClient(ILampClient* c) = 0; + virtual Status unregisterClient(ILampClient* c) = 0; + virtual void setLevel(uint16_t level, uint32_t fadeTime) = 0; + virtual uint16_t getLevel() = 0; + virtual bool isFading() = 0; + virtual void abortFading() = 0; +}; + +class IBus { +public: + enum class IBusState { + UNKNOWN, DISCONNECTED, CONNECTED + }; + + class IBusClient { + public: + virtual void onDataReceived(uint64_t timeMs, uint16_t data) = 0; + virtual void onBusStateChanged(IBusState state); + }; + + virtual Status registerClient(IBusClient* c) = 0; + virtual Status unregisterClient(IBusClient* c) = 0; + virtual Status sendAck(uint8_t ack) = 0; +}; + +class ITimer { +public: + class ITimerTask { + public: + virtual void timerTaskRun() = 0; + }; + + virtual uint64_t getTime() = 0; + virtual Status schedule(ITimerTask* task, uint32_t delay, uint32_t period) = 0; + virtual void cancel(ITimerTask* task) = 0; + virtual uint32_t randomize() = 0; +}; + +} // namespace dali + +#endif // DALI_DALI_HPP_ + diff --git a/src/dali/slave.cpp b/src/dali/slave.cpp new file mode 100644 index 0000000..655d9ed --- /dev/null +++ b/src/dali/slave.cpp @@ -0,0 +1,509 @@ +/* + * Copyright (c) 2015-2016, Arkadiusz Materek (arekmat@poczta.fm) + * + * Licensed under GNU General Public License 3.0 or later. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "slave.hpp" + +namespace dali { + +// static +Slave* Slave::create(IMemory* memoryDriver, ILamp* lampDriver, IBus* busDriver, ITimer* timer) { + controller::Memory* memory = new controller::Memory(memoryDriver); + controller::Lamp* lamp = new controller::Lamp(lampDriver, memory); + controller::QueryStore* queryStore = new controller::QueryStore(memory, lamp); + controller::Bus* bus = new controller::Bus(busDriver); + controller::Initialization* initialization = new controller::Initialization(timer, memory); + + return new Slave(memory, lamp, queryStore, bus, initialization); +} + +Slave::Slave(controller::Memory* memory, controller::Lamp* lamp, controller::QueryStore* queryStore, + controller::Bus* bus, controller::Initialization* initialization) : + mMemoryController(memory), mLampController(lamp), mQueryStoreController(queryStore), + mBusController(bus), mInitializationController(initialization), + mMemoryWriteEnabled(false), mDeviceType(0xff) { + mLampController->setListener(this); + mBusController->setListener(this); +} + +Slave::~Slave() { + mBusController->setListener(nullptr); + mLampController->setListener(nullptr); + delete mInitializationController; + delete mBusController; + delete mQueryStoreController; + delete mLampController; + delete mMemoryController; +} + +void Slave::notifyPowerUp() { + mLampController->powerRecallOnLevel(); +} + +void Slave::notifyPowerDown() { + mLampController->notifyPowerDown(); +} + +void Slave::onLampStateChnaged(ILamp::ILampState state) { + mInitializationController->onLampStateChnaged(state); +} + +uint8_t Slave::getShortAddr() { + return mMemoryController->getShortAddr(); +} + +uint16_t Slave::getGroups() { + return mMemoryController->getGroups(); +} + +void Slave::onBusDisconnected() { + mLampController->powerRecallFaliureLevel(); +} + +Status Slave::handleHandleDaliDeviceTypeCommand(uint16_t repeat, Command cmd, uint8_t param, uint8_t device_type) { + return Status::INVALID; +} + +Status Slave::handleCommand(uint16_t repeatCount, Command cmd, uint8_t param) { + // check memory write + switch (cmd) { + case Command::ENABLE_WRITE_MEMORY: + case Command::WRITE_MEMORY_LOCATION: + break; + + case Command::ENABLE_DEVICE_TYPE_X: + mDeviceType = param; + return Status::OK; + + default: + mMemoryWriteEnabled = false; + } + + Status status = internalHandleDaliDT8Command(repeatCount, cmd, param); + if (status != Status::REPEAT_REQUIRED) { + mDeviceType = 0xff; + } + return status; +} + +Status Slave::handleIgnoredCommand(Command cmd, uint8_t param) { + mDeviceType = 0xff; + return Status::INVALID; +} + +Status Slave::internalHandleDaliDT8Command(uint16_t repeatCount, Command cmd, uint8_t param) { + + // handle commands + switch (cmd) { + + case Command::OFF: + return mLampController->powerOff(); + + case Command::UP: + return mLampController->powerUp(); + + case Command::DOWN: + return mLampController->powerDown(); + + case Command::STEP_UP: + return mLampController->powerStepUp(); + + case Command::STEP_DOWN: + return mLampController->powerStepDown(); + + case Command::RECALL_MAX_LEVEL: + return mLampController->powerRecallMaxLevel(); + + case Command::RECALL_MIN_LEVEL: + return mLampController->powerRecallMinLevel(); + + case Command::STEP_DOWN_AND_OFF: + return mLampController->powerStepDownAndOff(); + + case Command::ON_AND_STEP_UP: + return mLampController->powerOnAndStepUp(); + + case Command::ENABLE_DAPC_SEQUENCE: + return mLampController->enableDapcSequence(mBusController->getLastCommandTimeMs()); + + case Command::GO_TO_SCENE_0: + case Command::GO_TO_SCENE_1: + case Command::GO_TO_SCENE_2: + case Command::GO_TO_SCENE_3: + case Command::GO_TO_SCENE_4: + case Command::GO_TO_SCENE_5: + case Command::GO_TO_SCENE_6: + case Command::GO_TO_SCENE_7: + case Command::GO_TO_SCENE_8: + case Command::GO_TO_SCENE_9: + case Command::GO_TO_SCENE_A: + case Command::GO_TO_SCENE_B: + case Command::GO_TO_SCENE_C: + case Command::GO_TO_SCENE_D: + case Command::GO_TO_SCENE_E: + case Command::GO_TO_SCENE_F: + return mLampController->powerScene(((uint8_t) cmd) & 0x0f); + + case Command::RESET: + if (repeatCount == 0) { + return Status::REPEAT_REQUIRED; + } + mInitializationController->reset(); + mQueryStoreController->reset(); + return Status::OK; + + case Command::STORE_ACTUAL_LEVEL_IN_DTR: + if (repeatCount == 0) { + return Status::REPEAT_REQUIRED; + } + return mQueryStoreController->storeActualLevelInDtr(); + + case Command::STORE_DTR_AS_MAX_LEVEL: + if (repeatCount == 0) { + return Status::REPEAT_REQUIRED; + } + return mQueryStoreController->storeDtrAsMaxLevel(); + + case Command::STORE_DTR_AS_MIN_LEVEL: + if (repeatCount == 0) { + return Status::REPEAT_REQUIRED; + } + return mQueryStoreController->storeDtrAsMinLevel(); + + case Command::STORE_DTR_AS_SYS_FAIL_LEVEL: + if (repeatCount == 0) { + return Status::REPEAT_REQUIRED; + } + return mQueryStoreController->storeDtrAsFailureLevel(); + + case Command::STORE_DTR_AS_POWER_ON_LEVEL: + if (repeatCount == 0) { + return Status::REPEAT_REQUIRED; + } + return mQueryStoreController->storePowerOnLevel(); + + case Command::STORE_DTR_AS_FADE_TIME: + if (repeatCount == 0) { + return Status::REPEAT_REQUIRED; + } + return mQueryStoreController->storeDtrAsFadeTime(); + + case Command::STORE_DTR_AS_FADE_RATE: + if (repeatCount == 0) { + return Status::REPEAT_REQUIRED; + } + return mQueryStoreController->storeDtrAsFadeRate(); + + case Command::STORE_DTR_AS_SCENE_0: + case Command::STORE_DTR_AS_SCENE_1: + case Command::STORE_DTR_AS_SCENE_2: + case Command::STORE_DTR_AS_SCENE_3: + case Command::STORE_DTR_AS_SCENE_4: + case Command::STORE_DTR_AS_SCENE_5: + case Command::STORE_DTR_AS_SCENE_6: + case Command::STORE_DTR_AS_SCENE_7: + case Command::STORE_DTR_AS_SCENE_8: + case Command::STORE_DTR_AS_SCENE_9: + case Command::STORE_DTR_AS_SCENE_A: + case Command::STORE_DTR_AS_SCENE_B: + case Command::STORE_DTR_AS_SCENE_C: + case Command::STORE_DTR_AS_SCENE_D: + case Command::STORE_DTR_AS_SCENE_E: + case Command::STORE_DTR_AS_SCENE_F: + if (repeatCount == 0) { + return Status::REPEAT_REQUIRED; + } + return mQueryStoreController->storeDtrAsScene(((uint8_t) cmd) & 0x0f); + + case Command::REMOVE_FROM_SCENE_0: + case Command::REMOVE_FROM_SCENE_1: + case Command::REMOVE_FROM_SCENE_2: + case Command::REMOVE_FROM_SCENE_3: + case Command::REMOVE_FROM_SCENE_4: + case Command::REMOVE_FROM_SCENE_5: + case Command::REMOVE_FROM_SCENE_6: + case Command::REMOVE_FROM_SCENE_7: + case Command::REMOVE_FROM_SCENE_8: + case Command::REMOVE_FROM_SCENE_9: + case Command::REMOVE_FROM_SCENE_A: + case Command::REMOVE_FROM_SCENE_B: + case Command::REMOVE_FROM_SCENE_C: + case Command::REMOVE_FROM_SCENE_D: + case Command::REMOVE_FROM_SCENE_E: + case Command::REMOVE_FROM_SCENE_F: + if (repeatCount == 0) { + return Status::REPEAT_REQUIRED; + } + return mQueryStoreController->removeFromScene(((uint8_t) cmd) & 0x0f); + + case Command::ADD_TO_GROUP_0: + case Command::ADD_TO_GROUP_1: + case Command::ADD_TO_GROUP_2: + case Command::ADD_TO_GROUP_3: + case Command::ADD_TO_GROUP_4: + case Command::ADD_TO_GROUP_5: + case Command::ADD_TO_GROUP_6: + case Command::ADD_TO_GROUP_7: + case Command::ADD_TO_GROUP_8: + case Command::ADD_TO_GROUP_9: + case Command::ADD_TO_GROUP_A: + case Command::ADD_TO_GROUP_B: + case Command::ADD_TO_GROUP_C: + case Command::ADD_TO_GROUP_D: + case Command::ADD_TO_GROUP_E: + case Command::ADD_TO_GROUP_F: + if (repeatCount == 0) { + return Status::REPEAT_REQUIRED; + } + return mQueryStoreController->addToGroup(((uint8_t) cmd) & 0x0f); + + case Command::REMOVE_FROM_GROUP_0: + case Command::REMOVE_FROM_GROUP_1: + case Command::REMOVE_FROM_GROUP_2: + case Command::REMOVE_FROM_GROUP_3: + case Command::REMOVE_FROM_GROUP_4: + case Command::REMOVE_FROM_GROUP_5: + case Command::REMOVE_FROM_GROUP_6: + case Command::REMOVE_FROM_GROUP_7: + case Command::REMOVE_FROM_GROUP_8: + case Command::REMOVE_FROM_GROUP_9: + case Command::REMOVE_FROM_GROUP_A: + case Command::REMOVE_FROM_GROUP_B: + case Command::REMOVE_FROM_GROUP_C: + case Command::REMOVE_FROM_GROUP_D: + case Command::REMOVE_FROM_GROUP_E: + case Command::REMOVE_FROM_GROUP_F: + if (repeatCount == 0) { + return Status::REPEAT_REQUIRED; + } + return mQueryStoreController->removeFromGroup(((uint8_t) cmd) & 0x0f); + + case Command::STORE_DTR_AS_SHORT_ADDR: + if (repeatCount == 0) { + return Status::REPEAT_REQUIRED; + } + return mQueryStoreController->storeDtrAsShortAddr(); + + case Command::ENABLE_WRITE_MEMORY: + if (repeatCount == 0) { + return Status::REPEAT_REQUIRED; + } + mMemoryWriteEnabled = true; + return Status::OK; + + case Command::QUERY_STATUS: + return sendAck(mQueryStoreController->queryStatus()); + + case Command::QUERY_CONTROL_GEAR: + return sendAck(DALI_ACK_YES); + + case Command::QUERY_LAMP_FAILURE: + if (mQueryStoreController->queryLampFailure()) { + return sendAck(DALI_ACK_YES); + } + return Status::OK; + + case Command::QUERY_LAMP_POWER_ON: + if (mQueryStoreController->queryLampPowerOn()) { + return sendAck(DALI_ACK_YES); + } + return Status::OK; + + case Command::QUERY_LIMIT_ERROR: + if (mQueryStoreController->queryLampLimitError()) { + return sendAck(DALI_ACK_YES); + } + return Status::OK; + + case Command::QUERY_RESET_STATE: + if (mQueryStoreController->queryResetState()) { + return sendAck(DALI_ACK_YES); + } + return Status::OK; + + case Command::QUERY_MISSING_SHORT_ADDR: { + if (mQueryStoreController->queryMissingShortAddr()) { + return sendAck(DALI_ACK_YES); + } + return Status::OK; + } + + case Command::QUERY_VERSION_NUMBER: + return sendAck(DALI_VERSION); + + case Command::QUERY_CONTENT_DTR: + return sendAck(mMemoryController->getDTR()); + + case Command::QUERY_DEVICE_TYPE: + return sendAck(DALI_DEVICE_TYPE); + + case Command::QUERY_PHISICAL_MIN_LEVEL: + return sendAck(mMemoryController->getPhisicalMinLevel()); + + case Command::QUERY_POWER_FAILURE: + if (!mQueryStoreController->queryLampPowerSet()) { + return sendAck(DALI_ACK_YES); + } + return Status::OK; + + case Command::QUERY_CONTENT_DTR1: + return sendAck(mMemoryController->getDTR1()); + + case Command::QUERY_CONTENT_DTR2: + return sendAck(mMemoryController->getDTR2()); + + case Command::QUERY_ACTUAL_LEVEL: + return sendAck(mQueryStoreController->queryActualLevel()); + + case Command::QUERY_MAX_LEVEL: + return sendAck(mQueryStoreController->queryMaxLevel()); + + case Command::QUERY_MIN_LEVEL: + return sendAck(mQueryStoreController->queryMinLevel()); + + case Command::QUERY_POWER_ON_LEVEL: + return sendAck(mQueryStoreController->queryPowerOnLevel()); + + case Command::QUERY_SYS_FAILURE_LEVEL: + return sendAck(mQueryStoreController->queryFaliureLevel()); + + case Command::QUERY_FADE_TIME_OR_RATE: + return sendAck(mQueryStoreController->queryFadeRateOrTime()); + + case Command::QUERY_SCENE_0_LEVEL: + case Command::QUERY_SCENE_1_LEVEL: + case Command::QUERY_SCENE_2_LEVEL: + case Command::QUERY_SCENE_3_LEVEL: + case Command::QUERY_SCENE_4_LEVEL: + case Command::QUERY_SCENE_5_LEVEL: + case Command::QUERY_SCENE_6_LEVEL: + case Command::QUERY_SCENE_7_LEVEL: + case Command::QUERY_SCENE_8_LEVEL: + case Command::QUERY_SCENE_9_LEVEL: + case Command::QUERY_SCENE_A_LEVEL: + case Command::QUERY_SCENE_B_LEVEL: + case Command::QUERY_SCENE_C_LEVEL: + case Command::QUERY_SCENE_D_LEVEL: + case Command::QUERY_SCENE_E_LEVEL: + case Command::QUERY_SCENE_F_LEVEL: + return sendAck(mQueryStoreController->queryLevelForScene(((uint8_t) cmd) & 0xf)); + + case Command::QUERY_GROUPS_L: + return sendAck(mQueryStoreController->queryGroupsL()); + + case Command::QUERY_GROUPS_H: + return sendAck(mQueryStoreController->queryGroupsH()); + + case Command::QUERY_RANDOM_ADDR_H: + return sendAck(mQueryStoreController->queryRandomAddrH()); + + case Command::QUERY_RANDOM_ADDR_M: + return sendAck(mQueryStoreController->queryRandomAddrM()); + + case Command::QUERY_RANDOM_ADDR_L: + return sendAck(mQueryStoreController->queryRandomAddrL()); + + case Command::READ_MEMORY_LOCATION: { + uint8_t data = 0; + if (mMemoryController->readMemory(&data) != Status::OK) { + return Status::ERROR; + } + return sendAck(data); + } + + // extended commands + + case Command::DIRECT_POWER_CONTROL: + return mLampController->powerDirect(param, mBusController->getLastCommandTimeMs()); + + case Command::TERMINATE: + return mInitializationController->terminate(); + + case Command::DATA_TRANSFER_REGISTER: + mMemoryController->setDTR(param); + return Status::OK; + + case Command::INITIALISE: + if (repeatCount == 0) { + return Status::REPEAT_REQUIRED; + } + return mInitializationController->initialize(param); + + case Command::RANDOMISE: + if (repeatCount == 0) { + return Status::REPEAT_REQUIRED; + } + return mInitializationController->randomize(); + + case Command::COMPARE: { + Status status = mInitializationController->compare(); + if (status == Status::OK) { + return mBusController->sendAck(DALI_ACK_YES); + } + return status; + } + case Command::WITHDRAW: + return mInitializationController->withdraw(); + + case Command::SEARCHADDRH: + return mInitializationController->searchAddrH(param); + + case Command::SEARCHADDRM: + return mInitializationController->searchAddrM(param); + + case Command::SEARCHADDRL: + return mInitializationController->searchAddrL(param); + + case Command::PROGRAM_SHORT_ADDRESS: + return mInitializationController->programShortAddr(param); + + case Command::VERIFY_SHORT_ADDRESS: { + Status status = mInitializationController->verifySortAddr(param); + if (status == Status::OK) { + return mBusController->sendAck(DALI_ACK_YES); + } + return status; + } + case Command::QUERY_SHORT_ADDRESS: { + uint8_t shortAddr; + Status status = mInitializationController->queryShortAddr(&shortAddr); + if (status == Status::OK) { + return mBusController->sendAck(shortAddr); + } + return status; + } + case Command::PHYSICAL_SELECTION: + return mInitializationController->physicalSelection(); + + case Command::DATA_TRANSFER_REGISTER_1: + mMemoryController->setDTR1(param); + return Status::OK; + + case Command::DATA_TRANSFER_REGISTER_2: + mMemoryController->setDTR2(param); + return Status::OK; + + case Command::WRITE_MEMORY_LOCATION: + if (!mMemoryWriteEnabled) { + return Status::ERROR; + } + switch (mMemoryController->writeMemory(param)) { + case Status::OK: + return sendAck(param); + default: + return Status::ERROR; + } + + default: + return handleHandleDaliDeviceTypeCommand(repeatCount, cmd, param, mDeviceType); + } +} + +} +// namespace dali diff --git a/src/dali/slave.hpp b/src/dali/slave.hpp new file mode 100644 index 0000000..398049b --- /dev/null +++ b/src/dali/slave.hpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2015-2016, Arkadiusz Materek (arekmat@poczta.fm) + * + * Licensed under GNU General Public License 3.0 or later. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef DALI_SLAVE_H_ +#define DALI_SLAVE_H_ + +#include +#include +#include +#include +#include +#include + +namespace dali { + +class Slave: public controller::Bus::Listener, controller::Lamp::Listener { +public: + static Slave* create(IMemory* memoryDriver, ILamp* lampDriver, IBus* busDriver, ITimer* timer); + + virtual ~Slave(); + + void notifyPowerUp(); + void notifyPowerDown(); + +private: + Slave(const Slave& other) = delete; + Slave& operator=(const Slave&) = delete; + + // LampController::Listener + void onLampStateChnaged(ILamp::ILampState state) override; + + // BusController::BusController + uint8_t getShortAddr() override; + uint16_t getGroups() override; + void onBusDisconnected() override; + Status handleCommand(uint16_t repeat, Command cmd, uint8_t param) override; + Status handleIgnoredCommand(Command cmd, uint8_t param) override; + Status internalHandleDaliDT8Command(uint16_t repeat, Command cmd, uint8_t param); + + Status sendAck(uint8_t ack) { + return mBusController->sendAck(ack); + } + + Status handleHandleDaliDeviceTypeCommand(uint16_t repeat, Command cmd, uint8_t param, uint8_t device_type); + + Slave(controller::Memory* memory, controller::Lamp* lamp, controller::QueryStore* queryStore, + controller::Bus* busDriver, controller::Initialization* initializationController); + + controller::Memory* const mMemoryController; + controller::Lamp* const mLampController; + controller::QueryStore* const mQueryStoreController; + controller::Bus* const mBusController; + controller::Initialization* const mInitializationController; + bool mMemoryWriteEnabled; + uint8_t mDeviceType; +}; + +} // namespace dali + +#endif // DALI_SLAVE_H_ diff --git a/src/util/manchester.cpp b/src/util/manchester.cpp new file mode 100644 index 0000000..798719e --- /dev/null +++ b/src/util/manchester.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2015-2016, Arkadiusz Materek (arekmat@poczta.fm) + * + * Licensed under GNU General Public License 3.0 or later. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "manchester.hpp" + +uint32_t manchesterEncode16(uint16_t data) { + uint32_t result = 0xffffffff; + for (uint8_t i = 0; i < 8; ++i) { + result <<= 2; + if (data & 0x80) { + result |= 1; + } else { + result |= 2; + } + data <<= 1; + } + return result; +} + +uint32_t manchesterEncode32(uint16_t data) { + uint32_t result = 0xffffffff; + for (uint8_t i = 0; i < 16; ++i) { + result <<= 2; + if (data & 0x8000) { + result |= 1; + } else { + result |= 2; + } + data <<= 1; + } + return result; +} + +uint32_t manchesterEncode16Inv(uint16_t data) { + uint32_t result = 0xffffffff; + for (uint8_t i = 0; i < 8; ++i) { + result <<= 2; + if (data & 0x01) { + result |= 2; + } else { + result |= 1; + } + data >>= 1; + } + return result; +} + +uint32_t manchesterEncode32Inv(uint16_t data) { + uint32_t result = 0xffffffff; + for (uint8_t i = 0; i < 16; ++i) { + result <<= 2; + if (data & 0x01) { + result |= 2; + } else { + result |= 1; + } + data >>= 1; + } + return result; +} + +uint16_t manchesterDecode32(uint32_t data) { + uint16_t result = 0x00000000; + for (uint8_t i = 0; i < 16; i++) { + uint16_t x = data >> 30; + switch (x) { + case 1: + result <<= 1; + result |= 1; + data <<= 2; + break; + + case 2: + result <<= 1; + data <<= 2; + break; + + default: + return 0xffff; + } + } + return result; +} + +uint16_t manchesterDecode16(uint32_t data) { + uint16_t result = 0x0000; + for (uint8_t i = 0; i < 8; i++) { + uint16_t x = (data & 0xffff) >> 14; + switch (x) { + case 1: + result <<= 1; + result |= 1; + data <<= 2; + break; + + case 2: + result <<= 1; + data <<= 2; + break; + + default: + return 0xffff; + } + } + return result; +} diff --git a/src/util/manchester.hpp b/src/util/manchester.hpp new file mode 100644 index 0000000..87fb809 --- /dev/null +++ b/src/util/manchester.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2015-2016, Arkadiusz Materek (arekmat@poczta.fm) + * + * Licensed under GNU General Public License 3.0 or later. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef UTIL_MANCHESTER_HPP_ +#define UTIL_MANCHESTER_HPP_ + +#include + +uint32_t manchesterEncode16(uint16_t data); +uint32_t manchesterEncode32(uint16_t data); +uint32_t manchesterEncode16Inv(uint16_t data); +uint32_t manchesterEncode32Inv(uint16_t data); +uint16_t manchesterDecode32(uint32_t data); +uint16_t manchesterDecode16(uint32_t data); + +#endif // UTIL_MANCHESTER_HPP_ diff --git a/src/xmc1200/bccu.cpp b/src/xmc1200/bccu.cpp new file mode 100644 index 0000000..d4a8d0e --- /dev/null +++ b/src/xmc1200/bccu.cpp @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2015-2016, Arkadiusz Materek (arekmat@poczta.fm) + * + * Licensed under GNU General Public License 3.0 or later. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "bccu.hpp" + +#include "bccu_config.h" +#include "clock.hpp" + +#define LINPRES_MAX 1023 +#define LINPRES_MAGIC 8192 + +#define DIMMING_MAX 1023 +#define DIMMING_MAGIC_UP 20479 +#define DIMMING_MAGIC_DOWN 20734 +#define DIMMING_MAGIC ((DIMMING_MAGIC_UP + DIMMING_MAGIC_DOWN) / 2) + +#define MSEC_PER_SEC 1000 + +namespace xmc { +namespace { + +BCCU_Type* const BCCU = BCCU0; + +BCCU_DE_Type* const BCCU_DEs[] = { BCCU0_DE0, BCCU0_DE1, BCCU0_DE2 }; + +BCCU_CH_Type* const BCCU_CHs[] = { + BCCU0_CH0, + BCCU0_CH1, + BCCU0_CH2, + BCCU0_CH3, + BCCU0_CH4, + BCCU0_CH5, + BCCU0_CH6, + BCCU0_CH7, + BCCU0_CH8, }; + +uint16_t calculateDimmingPrescaller(uint32_t timeMs, uint32_t magic) { + uint32_t dclk_ps = BCCU->GLOBCLK & BCCU_GLOBCLK_DCLK_PS_Msk; + dclk_ps >>= BCCU_GLOBCLK_DCLK_PS_Pos; + + uint32_t prescaler = ((uint32_t) (CPU_CLOCK / MSEC_PER_SEC) * timeMs / dclk_ps + magic / 2) / magic; + if (prescaler > DIMMING_MAX) { + return 0; + } + return (uint16_t) prescaler; +} + +uint16_t calculateLinearPrescaller(uint32_t timeMs) { + uint32_t fclk_ps = BCCU->GLOBCLK & BCCU_GLOBCLK_FCLK_PS_Msk; + fclk_ps >>= BCCU_GLOBCLK_FCLK_PS_Pos; + + uint32_t prescaler = ((uint32_t) (CPU_CLOCK / MSEC_PER_SEC) * timeMs / fclk_ps + LINPRES_MAGIC / 2) / LINPRES_MAGIC; + if (prescaler > LINPRES_MAX) { + return 0; + } + return (uint16_t) prescaler; +} + +bool gBccuConfigured = false; + +void configureBccuGlobal() { + if (gBccuConfigured) { + return; + } + + XMC_BCCU_GlobalInit(BCCU, &kBCCUGlobalConfig); + BCCU->CHTRIG = 0; + +#ifdef XMC_BCCU_CH0_PIN + XMC_GPIO_SetMode(XMC_BCCU_CH0_PIN, XMC_BCCU_CH0_PIN_MODE); +#endif +#ifdef XMC_BCCU_CH1_PIN + XMC_GPIO_SetMode(XMC_BCCU_CH1_PIN, XMC_BCCU_CH1_PIN_MODE); +#endif +#ifdef XMC_BCCU_CH2_PIN + XMC_GPIO_SetMode(XMC_BCCU_CH2_PIN, XMC_BCCU_CH2_PIN_MODE); +#endif +#ifdef XMC_BCCU_CH3_PIN + XMC_GPIO_SetMode(XMC_BCCU_CH3_PIN, XMC_BCCU_CH3_PIN_MODE); +#endif +#ifdef XMC_BCCU_CH4_PIN + XMC_GPIO_SetMode(XMC_BCCU_CH4_PIN, XMC_BCCU_CH4_PIN_MODE); +#endif +#ifdef XMC_BCCU_CH5_PIN + XMC_GPIO_SetMode(XMC_BCCU_CH5_PIN, XMC_BCCU_CH5_PIN_MODE); +#endif +#ifdef XMC_BCCU_CH6_PIN + XMC_GPIO_SetMode(XMC_BCCU_CH6_PIN, XMC_BCCU_CH6_PIN_MODE); +#endif +#ifdef XMC_BCCU_CH7_PIN + XMC_GPIO_SetMode(XMC_BCCU_CH7_PIN, XMC_BCCU_CH7_PIN_MODE); +#endif +#ifdef XMC_BCCU_CH8_PIN + XMC_GPIO_SetMode(XMC_BCCU_CH8_PIN, XMC_BCCU_CH8_PIN_MODE); +#endif + + gBccuConfigured = true; +} + +void configureBccuDimmingEngine(BCCU_DE_Type* BCCU_DE) { + configureBccuGlobal(); + + XMC_BCCU_DIM_Init(BCCU_DE, &kBCCUDimmingConfig); + XMC_BCCU_DIM_SetTargetDimmingLevel(BCCU_DE, 0); +} + +void configureBccuChannel(BCCU_CH_Type* BCCU_CH, Bccu::DimmingEngine engine, + const XMC_BCCU_CH_CONFIG_t* channelConfig) { + XMC_BCCU_CH_Init(BCCU_CH, channelConfig); + XMC_BCCU_CH_SelectDimEngine(BCCU_CH, (XMC_BCCU_CH_DIMMING_SOURCE_t) engine); +} + +} // namespace + +Bccu::Bccu(DimmingEngine de, uint32_t engineMask, uint32_t channelsMask) : + BCCU_DE(BCCU_DEs[de]), mEngineMask(engineMask), mChannelsMask(channelsMask), mLastFadeTime(0), mLastChangeTime(0) { +} + +void Bccu::enable() { + XMC_BCCU_ConcurrentEnableChannels(BCCU, mChannelsMask); + for (uint16_t i = 0; i < 9; ++i) { + if (mChannelsMask & (1 << i)) { + BCCU_CH_Type* BCCU_CH = BCCU_CHs[i]; + XMC_BCCU_CH_SetLinearWalkPrescaler(BCCU_CH, 0); + XMC_BCCU_CH_SetTargetIntensity(BCCU_CH, 0); + } + } + XMC_BCCU_ConcurrentStartLinearWalk(BCCU, mChannelsMask); + XMC_BCCU_ConcurrentEnableDimmingEngine(BCCU, mEngineMask); + XMC_BCCU_ConcurrentStartDimming(BCCU, mEngineMask); +} + +void Bccu::disable() { + XMC_BCCU_ConcurrentAbortLinearWalk(BCCU, mChannelsMask); + XMC_BCCU_ConcurrentDisableChannels(BCCU, mChannelsMask); + + XMC_BCCU_ConcurrentAbortDimming(BCCU, mEngineMask); + XMC_BCCU_DIM_SetTargetDimmingLevel(BCCU_DE, 0); + XMC_BCCU_ConcurrentStartDimming(BCCU, mEngineMask); + while (isFading()) { + } + XMC_BCCU_ConcurrentDisableDimmingEngine(BCCU, mEngineMask); +} + +void Bccu::setLevel(uint16_t level, uint32_t fadeTime) { + // ASSERT(fadeTime < 2^31) + if (isFading()) { + abortFading(); + } + uint32_t up = getLevel() > level ? 1 : -1; + int32_t _fadeTime = up * (int32_t) fadeTime; + if (mLastFadeTime != _fadeTime) { + mLastFadeTime = _fadeTime; + uint32_t prescaler = calculateDimmingPrescaller(fadeTime, up ? DIMMING_MAGIC_UP : DIMMING_MAGIC_DOWN); + XMC_BCCU_DIM_SetDimDivider(BCCU_DE, prescaler); + } + XMC_BCCU_DIM_SetTargetDimmingLevel(BCCU_DE, level); + XMC_BCCU_ConcurrentStartDimming(BCCU, mEngineMask); +} + +uint16_t Bccu::getLevel() { + return XMC_BCCU_DIM_ReadDimmingLevel(BCCU_DE); +} + +bool Bccu::isFading() { + return (BCCU->DESTRCON & mEngineMask) != 0; +} + +void Bccu::abortFading() { + XMC_BCCU_ConcurrentAbortDimming(BCCU, mEngineMask); +} + +bool Bccu::isColorChanging() { + return (BCCU->CHSTRCON & mChannelsMask) != 0; +} + +void Bccu::abortColorChanging() { + XMC_BCCU_ConcurrentAbortLinearWalk(BCCU, mChannelsMask); +} + +BccuLampRGB::BccuLampRGB(DimmingEngine de, Channel r, Channel g, Channel b, const XMC_BCCU_CH_CONFIG_t* channelConfigR, + const XMC_BCCU_CH_CONFIG_t* channelConfigG, const XMC_BCCU_CH_CONFIG_t* channelConfigB) : + Bccu(de, (1 << de), ((1 << r) | (1 << g) | (1 << b))), BCCU_CH_R(BCCU_CHs[r]), BCCU_CH_G(BCCU_CHs[g]), + BCCU_CH_B(BCCU_CHs[b]) { + configureBccuDimmingEngine(BCCU_DE); + configureBccuChannel(BCCU_CH_R, de, channelConfigR); + configureBccuChannel(BCCU_CH_G, de, channelConfigG); + configureBccuChannel(BCCU_CH_B, de, channelConfigB); +} + +void BccuLampRGB::setColor(uint16_t r, uint16_t g, uint16_t b, uint32_t changeTime) { + if (isColorChanging()) { + abortColorChanging(); + } + if (mLastChangeTime != (int32_t) changeTime) { + mLastChangeTime = (int32_t) changeTime; + uint32_t prescaler = calculateLinearPrescaller(changeTime); + XMC_BCCU_CH_SetLinearWalkPrescaler(BCCU_CH_R, prescaler); + XMC_BCCU_CH_SetLinearWalkPrescaler(BCCU_CH_G, prescaler); + XMC_BCCU_CH_SetLinearWalkPrescaler(BCCU_CH_B, prescaler); + } + + XMC_BCCU_CH_SetTargetIntensity(BCCU_CH_R, r); + XMC_BCCU_CH_SetTargetIntensity(BCCU_CH_G, g); + XMC_BCCU_CH_SetTargetIntensity(BCCU_CH_B, b); + + XMC_BCCU_ConcurrentStartLinearWalk(BCCU, mChannelsMask); +} + +uint16_t BccuLampRGB::getColorR() { + return XMC_BCCU_CH_ReadIntensity(BCCU_CH_R); +} + +uint16_t BccuLampRGB::getColorG() { + return XMC_BCCU_CH_ReadIntensity(BCCU_CH_G); +} + +uint16_t BccuLampRGB::getColorB() { + return XMC_BCCU_CH_ReadIntensity(BCCU_CH_B); +} + +} // namespace xmc diff --git a/src/xmc1200/bccu.hpp b/src/xmc1200/bccu.hpp new file mode 100644 index 0000000..7f917e6 --- /dev/null +++ b/src/xmc1200/bccu.hpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2015-2016, Arkadiusz Materek (arekmat@poczta.fm) + * + * Licensed under GNU General Public License 3.0 or later. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef XMC_LAMP_HPP_ +#define XMC_LAMP_HPP_ + +#include + +namespace xmc { + +class Bccu { +public: + + typedef enum { + DE0 = 0, // BCCU Dimming Engine 0 + DE1 = 1, // BCCU Dimming Engine 1 + DE2 = 2, // BCCU Dimming Engine 2 + } DimmingEngine; + + typedef enum { + CH0 = 0, // BCCU Channel 0 + CH1 = 1, // BCCU Channel 1 + CH2 = 2, // BCCU Channel 2 + CH3 = 3, // BCCU Channel 3 + CH4 = 4, // BCCU Channel 4 + CH5 = 5, // BCCU Channel 5 + CH6 = 6, // BCCU Channel 6 + CH7 = 7, // BCCU Channel 7 + CH8 = 8, // BCCU Channel 8 + } Channel; + + Bccu(DimmingEngine de, uint32_t engineMask, uint32_t channelsMask); + + void enable(); + void disable(); + + void setLevel(uint16_t level, uint32_t fadeTime); + uint16_t getLevel(); + + bool isFading(); + void abortFading(); + bool isColorChanging(); + void abortColorChanging(); + +protected: + BCCU_DE_Type* const BCCU_DE; + const uint32_t mEngineMask; + const uint32_t mChannelsMask; + int32_t mLastFadeTime; // not unsigned because up/down are different + int32_t mLastChangeTime; +}; + + +class BccuLampRGB: public Bccu { +public: + BccuLampRGB(DimmingEngine de, Channel r, Channel g, Channel b, const XMC_BCCU_CH_CONFIG_t* channelConfigR, + const XMC_BCCU_CH_CONFIG_t* channelConfigG, const XMC_BCCU_CH_CONFIG_t* channelConfigB); + + ~BccuLampRGB() { + disable(); + } + + void setColor(uint16_t R, uint16_t G, uint16_t B, uint32_t changeTime); + uint16_t getColorR(); + uint16_t getColorG(); + uint16_t getColorB(); + +private: + BCCU_CH_Type* BCCU_CH_R; + BCCU_CH_Type* BCCU_CH_G; + BCCU_CH_Type* BCCU_CH_B; +}; + +}// namespace xmc + +#endif // XMC_LAMP_HPP_ diff --git a/src/xmc1200/bccu_config.c b/src/xmc1200/bccu_config.c new file mode 100644 index 0000000..afbccd9 --- /dev/null +++ b/src/xmc1200/bccu_config.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2015-2016, Arkadiusz Materek (arekmat@poczta.fm) + * + * Licensed under GNU General Public License 3.0 or later. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "bccu_config.h" + +const XMC_BCCU_GLOBAL_CONFIG_t kBCCUGlobalConfig = { + trig_mode: XMC_BCCU_TRIGMODE0, + trig_delay: XMC_BCCU_TRIGDELAY_NO_DELAY, + maxzero_at_output: 256, + fclk_ps: 346, + bclk_sel: XMC_BCCU_BCLK_MODE_NORMAL, + dclk_ps: 154, + global_dimlevel: 0, +}; + +const XMC_BCCU_DIM_CONFIG_t kBCCUDimmingConfig = { + dim_div: 0, + dither_en: 0, + cur_sel: XMC_BCCU_DIM_CURVE_FINE, +}; + +const XMC_BCCU_CH_CONFIG_t kLampBCCUChannelConfig = { + pack_thresh: 3, + pack_en: 0, + dim_sel: XMC_BCCU_CH_DIMMING_SOURCE_GLOBAL, + dim_bypass: XMC_BCCU_CH_DIMMING_ENGINE_BYPASS_DISABLE, + gate_en: XMC_BCCU_CH_GATING_FUNC_DISABLE, + flick_wd_en: XMC_BCCU_CH_FLICKER_WD_EN, + trig_edge: XMC_BCCU_CH_TRIG_EDGE_PASS_TO_ACT, + force_trig_en: 0, + pack_offcmp_lev: 55, + pack_oncmp_lev: 55, + pack_offcnt_val: 0, + pack_oncnt_val: 0, +}; diff --git a/src/xmc1200/bccu_config.h b/src/xmc1200/bccu_config.h new file mode 100644 index 0000000..1079688 --- /dev/null +++ b/src/xmc1200/bccu_config.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2015-2016, Arkadiusz Materek (arekmat@poczta.fm) + * + * Licensed under GNU General Public License 3.0 or later. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef XMC_LAMP_CONFIG_H_ +#define XMC_LAMP_CONFIG_H_ + + +#include +#include + +// remove unused pins +#define XMC_BCCU_CH0_PIN P0_4 +#define XMC_BCCU_CH0_PIN_MODE XMC_GPIO_MODE_OUTPUT_PUSH_PULL_ALT1 + +#define XMC_BCCU_CH1_PIN P0_5 +#define XMC_BCCU_CH1_PIN_MODE XMC_GPIO_MODE_OUTPUT_PUSH_PULL_ALT1 + +#define XMC_BCCU_CH2_PIN P0_6 +#define XMC_BCCU_CH2_PIN_MODE XMC_GPIO_MODE_OUTPUT_PUSH_PULL_ALT1 + +#define XMC_BCCU_CH3_PIN P0_7 +#define XMC_BCCU_CH3_PIN_MODE XMC_GPIO_MODE_OUTPUT_PUSH_PULL_ALT1 + +#define XMC_BCCU_CH4_PIN P0_8 +#define XMC_BCCU_CH4_PIN_MODE XMC_GPIO_MODE_OUTPUT_PUSH_PULL_ALT1 + +#define XMC_BCCU_CH5_PIN P0_9 +#define XMC_BCCU_CH5_PIN_MODE XMC_GPIO_MODE_OUTPUT_PUSH_PULL_ALT1 + +#define XMC_BCCU_CH6_PIN P0_10 +#define XMC_BCCU_CH6_PIN_MODE XMC_GPIO_MODE_OUTPUT_PUSH_PULL_ALT1 + +#define XMC_BCCU_CH7_PIN P0_11 +#define XMC_BCCU_CH7_PIN_MODE XMC_GPIO_MODE_OUTPUT_PUSH_PULL_ALT1 + +#define XMC_BCCU_CH8_PIN P0_1 +#define XMC_BCCU_CH8_PIN_MODE XMC_GPIO_MODE_OUTPUT_PUSH_PULL_ALT6 + +#ifdef __cplusplus +extern "C" { +#endif + +extern const XMC_BCCU_GLOBAL_CONFIG_t kBCCUGlobalConfig; +extern const XMC_BCCU_DIM_CONFIG_t kBCCUDimmingConfig; +extern const XMC_BCCU_CH_CONFIG_t kLampBCCUChannelConfig; + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // XMC_LAMP_CONFIG_H_ diff --git a/src/xmc1200/clock.cpp b/src/xmc1200/clock.cpp new file mode 100644 index 0000000..5e10070 --- /dev/null +++ b/src/xmc1200/clock.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2015-2016, Arkadiusz Materek (arekmat@poczta.fm) + * + * Licensed under GNU General Public License 3.0 or later. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "clock.hpp" + +#include +#include + +namespace xmc { + +void Clock::init(Clock::Frequency freq) { + XMC_SCU_CLOCK_CONFIG_t config; + config.pclk_src = XMC_SCU_CLOCK_PCLKSRC_MCLK; + config.rtc_src = XMC_SCU_CLOCK_RTCCLKSRC_DCO2; + config.fdiv = 0; + config.idiv = freq; + XMC_SCU_CLOCK_Init(&config); +} + +uint32_t Clock::freq(void) { + return SystemCoreClock; +} + +} // namespace xmc diff --git a/src/xmc1200/clock.hpp b/src/xmc1200/clock.hpp new file mode 100644 index 0000000..51b48a9 --- /dev/null +++ b/src/xmc1200/clock.hpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2015-2016, Arkadiusz Materek (arekmat@poczta.fm) + * + * Licensed under GNU General Public License 3.0 or later. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef XMC_CLK_HPP_ +#define XMC_CLK_HPP_ + +#include + +namespace xmc { + +class Clock { +public: + + typedef enum { + FREQ_32MHZ = 0x01, + FREQ_16MHZ = 0x02, + FREQ_10_67MHZ = 0x03, + FREQ_8MHZ = 0x04, + FREQ_126KHZ = 0xFE, + FREQ_125_5KHZ = 0xFF, + } Frequency; + + // Initializes SCU Clock registers based on user configuration + static void init(Frequency freq); + + // Returns current clock frequency + static uint32_t freq(void); + +}; +// class Clock + +}// namespace xmc + +#endif // XMC_CLK_HPP_ diff --git a/src/xmc1200/dali/bus.cpp b/src/xmc1200/dali/bus.cpp new file mode 100644 index 0000000..d51e6fb --- /dev/null +++ b/src/xmc1200/dali/bus.cpp @@ -0,0 +1,384 @@ +/* + * Copyright (c) 2015-2016, Arkadiusz Materek (arekmat@poczta.fm) + * + * Licensed under GNU General Public License 3.0 or later. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "bus.hpp" + +#include "bus_config.h" +#include "timer.hpp" + +#include + +using namespace ::dali; + +namespace dali { +namespace xmc { + +namespace { + +#define PULSE_GLITCH 500 + +#define PULSE_TIME (13333) +#define PULSE_TIME_MIN (8000) +#define PULSE_TIME_MAX (18000) + +#define PULSE_TIME_SHORT_MIN PULSE_TIME_MIN +#define PULSE_TIME_SHORT_MAX PULSE_TIME_MAX +#define PULSE_TIME_LONG_MIN (PULSE_TIME_MIN * 2) +#define PULSE_TIME_LONG_MAX (PULSE_TIME_MAX * 2) + +#define INVALID32 0xffffffffL +#define INVALID16 0xffff + +enum class RxState { + IDLE, START_LOW, START_HIGHT, DATA_LOW, DATA_HIGHT, HAVE_DATA, ERROR +}; + +uint8_t gRxDataBit = 0; +volatile RxState gRxState; +volatile uint32_t gRxData32 = INVALID32; +volatile uint32_t gRxDataTmp; +uint16_t gTxData = INVALID16; +volatile uint64_t gBusLowTime = 0; + +#define BUS_TIME_CONNECTED 0xffffffffffffffffUL + +#define MAX_CLIENTS 1 + +IBus::IBusState gBusState = IBus::IBusState::UNKNOWN; +IBus::IBusClient* gClients[MAX_CLIENTS]; + +void onRisingEdge(uint16_t timer) { + gBusLowTime = BUS_TIME_CONNECTED; + + if (timer == 0) { + return; + } + + if (timer <= PULSE_GLITCH) { + return; + } + + bool longPulse; + if ((timer >= PULSE_TIME_SHORT_MIN) && (timer <= PULSE_TIME_SHORT_MAX)) { + longPulse = false; + } else if ((timer >= PULSE_TIME_LONG_MIN) && (timer <= PULSE_TIME_LONG_MAX)) { + longPulse = true; + } else { + gRxState = RxState::ERROR; + return; + } + + switch (gRxState) { + case RxState::START_LOW: // 2: check first half of start bit + if (longPulse) { + gRxState = RxState::ERROR; + return; + } + gRxState = RxState::START_HIGHT; + return; + + case RxState::DATA_LOW: + gRxState = RxState::DATA_HIGHT; + if (longPulse) { + gRxDataBit += 2; + gRxDataTmp <<= 2; + } else { + gRxDataBit += 1; + gRxDataTmp <<= 1; + } + return; + + default: + return; + } +} + +void onFallingEdge(uint16_t timer) { + gBusLowTime = Timer::getTimeMs(); + + if (timer == 0) { + gRxState = RxState::START_LOW; + return; + } + + if (timer <= PULSE_GLITCH) { + return; + } + + bool longPulse; + if ((timer >= PULSE_TIME_SHORT_MIN) && (timer <= PULSE_TIME_SHORT_MAX)) { + longPulse = false; + } else if ((timer >= PULSE_TIME_LONG_MIN) && (timer <= PULSE_TIME_LONG_MAX)) { + longPulse = true; + } else { + gRxState = RxState::ERROR; + return; + } + + switch (gRxState) { + case RxState::START_HIGHT: + gRxState = RxState::DATA_LOW; + if (longPulse) { + gRxDataBit = 1; + gRxDataTmp = 1; + } else { + gRxDataBit = 0; + gRxDataTmp = 0; + } + return; + + case RxState::DATA_HIGHT: + gRxState = RxState::DATA_LOW; + if (longPulse) { + gRxDataBit += 2; + gRxDataTmp <<= 2; + gRxDataTmp |= 3; + } else { + gRxDataBit += 1; + gRxDataTmp <<= 1; + gRxDataTmp |= 1; + } + return; + + default: + return; + } +} + +void onTimeOut() { + if (gRxState == RxState::ERROR) { + gRxDataBit = -1; // prevent unexpected data + } + + gRxState = RxState::IDLE; + gRxData32 = INVALID32; + + switch (gRxDataBit) { + case 32: + gRxData32 = gRxDataTmp; + break; + + case 32 - 1: + gRxData32 = gRxDataTmp; + gRxData32 <<= 1; + gRxData32 |= 1; + break; + } +} + +} // namespace + +//static +Bus* Bus::getInstance() { + static Bus gBus; + return &gBus; +} + +Bus::Bus() { + Bus::initRx(); + Bus::initTx(); +} + +Bus::~Bus() { +// TODO clean up +} + +Status Bus::registerClient(IBusClient* c) { + for (uint16_t i = 0; i < MAX_CLIENTS; ++i) { + if (gClients[i] == nullptr) { + gClients[i] = c; + c->onBusStateChanged(gBusState); + return Status::OK; + } + } + return Status::ERROR; +} + +Status Bus::unregisterClient(IBusClient* c) { + for (uint16_t i = 0; i < MAX_CLIENTS; ++i) { + if (gClients[i] == c) { + gClients[i] = nullptr; + return Status::OK; + } + } + return Status::ERROR; +} + +Status Bus::sendAck(uint8_t ack) { + Bus::tx(ack); + return Status::OK; +} + +void Bus::runSlice() { + uint16_t data; + uint64_t time = Timer::getTimeMs(); + + __disable_irq(); + uint64_t busLowTime = gBusLowTime; + __enable_irq(); + + if (busLowTime == BUS_TIME_CONNECTED) { + if (gBusState != IBus::IBusState::CONNECTED) { + onBusStateChanged(IBus::IBusState::CONNECTED); + } + } else { + if (time - busLowTime >= 500) { + if (gBusState != IBus::IBusState::DISCONNECTED) { + onBusStateChanged(IBus::IBusState::DISCONNECTED); + } + } + } + + if (Bus::checkRxTx(time, &data)) { + onDataReceived(time, data); + } +} + +// static +void Bus::onDataReceived(uint64_t time, uint16_t data) { + for (uint8_t i = 0; i < MAX_CLIENTS; ++i) { + if (gClients[i] != nullptr) { + gClients[i]->onDataReceived(time, data); + } + } +} + +// static +void Bus::onBusStateChanged(IBusState state) { + gBusState = state; + + for (uint8_t i = 0; i < MAX_CLIENTS; ++i) { + if (gClients[i] != nullptr) { + gClients[i]->onBusStateChanged(state); + } + } +} + +//static +void Bus::initRx() { + XMC_GPIO_SetMode(CCU40_RX_PIN, DALI_XMC_CCU40_RX_PIN_MODE); + + XMC_CCU4_Init(CCU40, XMC_CCU4_SLICE_MCMS_ACTION_TRANSFER_PR_CR); + XMC_CCU4_StartPrescaler(CCU40); + XMC_CCU4_SetModuleClock(CCU40, XMC_CCU4_CLOCK_SCU); + + XMC_CCU4_SLICE_CaptureInit(CCU40_SLICE, &kDaliRxCCU4CaptureConfig); + + XMC_CCU4_SLICE_SetTimerPeriodMatch(CCU40_SLICE, 65535); + XMC_CCU4_EnableShadowTransfer(CCU40, CCU40_SLICE_SHADDOW_TRANSFER); + + XMC_CCU4_SLICE_Capture0Config(CCU40_SLICE, XMC_CCU4_SLICE_EVENT_0); + XMC_CCU4_SLICE_Capture1Config(CCU40_SLICE, XMC_CCU4_SLICE_EVENT_1); + XMC_CCU4_SLICE_ConfigureEvent(CCU40_SLICE, XMC_CCU4_SLICE_EVENT_0, &kDaliRxCCU4CaptureRisingConfig); + XMC_CCU4_SLICE_ConfigureEvent(CCU40_SLICE, XMC_CCU4_SLICE_EVENT_1, &kDaliRxCCU4CaptureFallingConfig); + + XMC_CCU4_SLICE_EnableEvent(CCU40_SLICE, XMC_CCU4_SLICE_IRQ_ID_PERIOD_MATCH); + XMC_CCU4_SLICE_EnableEvent(CCU40_SLICE, XMC_CCU4_SLICE_IRQ_ID_EVENT0); + XMC_CCU4_SLICE_EnableEvent(CCU40_SLICE, XMC_CCU4_SLICE_IRQ_ID_EVENT1); + + XMC_CCU4_SLICE_SetInterruptNode(CCU40_SLICE, XMC_CCU4_SLICE_IRQ_ID_PERIOD_MATCH, XMC_CCU4_SLICE_SR_ID_1); + XMC_CCU4_SLICE_SetInterruptNode(CCU40_SLICE, XMC_CCU4_SLICE_IRQ_ID_EVENT0, XMC_CCU4_SLICE_SR_ID_2); + XMC_CCU4_SLICE_SetInterruptNode(CCU40_SLICE, XMC_CCU4_SLICE_IRQ_ID_EVENT1, XMC_CCU4_SLICE_SR_ID_3); + + NVIC_SetPriority(CCU40_1_IRQn, 2); + NVIC_SetPriority(CCU40_2_IRQn, 3); + NVIC_SetPriority(CCU40_3_IRQn, 3); + NVIC_EnableIRQ(CCU40_1_IRQn); + NVIC_EnableIRQ(CCU40_2_IRQn); + NVIC_EnableIRQ(CCU40_3_IRQn); + + XMC_CCU4_EnableClock(CCU40, CCU40_SLICE_NUMBER); +} + +//static +bool Bus::checkRxTx(uint64_t time, uint16_t* data) { + static uint64_t gLastDataTime = 0; + bool rxResult = false; + __disable_irq(); + uint32_t rxData32 = gRxData32; + gRxData32 = INVALID32; + __enable_irq(); + if (rxData32 != INVALID32) { + gLastDataTime = time; + + *data = manchesterDecode32(rxData32); + rxResult = true; + } + + if (gTxData != INVALID16) { + uint64_t dTime = time - gLastDataTime; + if (dTime > 3) { + if (gRxState == RxState::IDLE) { + uint16_t tmpTxData = gTxData; + gTxData = INVALID16; + uint32_t txData = manchesterEncode16Inv(tmpTxData); + txData <<= 1; + txData |= 0x01; + XMC_UART_CH_Transmit(DALI_UART_CH, (uint16_t) (txData & 0xffff)); + XMC_UART_CH_Transmit(DALI_UART_CH, (uint16_t) (txData >> 16)); + } else { + gTxData = INVALID16; + } + } + } + return rxResult; +} + +// static +void Bus::initTx() { + XMC_UART_CH_Init(DALI_UART_CH, &kDaliTxUARTConfig); + XMC_USIC_CH_TXFIFO_Configure(DALI_UART_CH, 0, XMC_USIC_CH_FIFO_DISABLED, 0); + XMC_UART_CH_Start(DALI_UART_CH); + XMC_GPIO_SetMode(DALI_UART_TX_PIN, DALI_UART_TX_PIN_MODE); +} + +// static +void Bus::tx(uint8_t data) { + if (gTxData == INVALID16) { + gTxData = data; + } +} + +extern "C" { + +void CCU40_1_IRQHandler(void) { + XMC_CCU4_SLICE_ClearEvent(CCU40_SLICE, XMC_CCU4_SLICE_IRQ_ID_PERIOD_MATCH); + onTimeOut(); +} + +void CCU40_2_IRQHandler(void) { + uint32_t time0 = XMC_CCU4_SLICE_GetCaptureRegisterValue(CCU40_SLICE, 0); + uint32_t time1 = XMC_CCU4_SLICE_GetCaptureRegisterValue(CCU40_SLICE, 1); + XMC_CCU4_SLICE_ClearEvent(CCU40_SLICE, XMC_CCU4_SLICE_IRQ_ID_EVENT0); + XMC_CCU4_SLICE_StartTimer(CCU40_SLICE); + if (time0 & CCU4_CC4_CV_FFL_Msk) { + onRisingEdge(time0 & CCU4_CC4_CV_CAPTV_Msk); + } else if (time1 & CCU4_CC4_CV_FFL_Msk) { + onRisingEdge(time1 & CCU4_CC4_CV_CAPTV_Msk); + } +} + +void CCU40_3_IRQHandler(void) { + uint32_t time0 = XMC_CCU4_SLICE_GetCaptureRegisterValue(CCU40_SLICE, 2); + uint32_t time1 = XMC_CCU4_SLICE_GetCaptureRegisterValue(CCU40_SLICE, 3); + XMC_CCU4_SLICE_ClearEvent(CCU40_SLICE, XMC_CCU4_SLICE_IRQ_ID_EVENT1); + XMC_CCU4_SLICE_StartTimer(CCU40_SLICE); + if (time0 & CCU4_CC4_CV_FFL_Msk) { + onFallingEdge(time0 & CCU4_CC4_CV_CAPTV_Msk); + } else if (time1 & CCU4_CC4_CV_FFL_Msk) { + onFallingEdge(time1 & CCU4_CC4_CV_CAPTV_Msk); + } +} + +} // extern "C" + +} // namespace xmc +} // namespace dali + diff --git a/src/xmc1200/dali/bus.hpp b/src/xmc1200/dali/bus.hpp new file mode 100644 index 0000000..e19a15e --- /dev/null +++ b/src/xmc1200/dali/bus.hpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2015-2016, Arkadiusz Materek (arekmat@poczta.fm) + * + * Licensed under GNU General Public License 3.0 or later. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef XMC_DALI_BUS_HPP_ +#define XMC_DALI_BUS_HPP_ + +#include + +namespace dali { +namespace xmc { + +class Bus: public dali::IBus { +public: + static Bus* getInstance(); + + dali::Status registerClient(IBusClient* c) override; + dali::Status unregisterClient(IBusClient* c) override; + dali::Status sendAck(uint8_t ack) override; + + static void runSlice(); + +private: + Bus(); + Bus(const Bus& other) = delete; + Bus& operator=(const Bus&) = delete; + + ~Bus(); + + static void onDataReceived(uint64_t timeMs, uint16_t data); + static void onBusStateChanged(IBusState state); + + static void initRx(); + static bool checkRxTx(uint64_t time, uint16_t* data); + + static void initTx(); + static void tx(uint8_t data); +}; + +} // namespace xmc +} // namespace dali + +#endif // XMC_DALI_BUS_HPP_ diff --git a/src/xmc1200/dali/bus_config.c b/src/xmc1200/dali/bus_config.c new file mode 100644 index 0000000..546d6ae --- /dev/null +++ b/src/xmc1200/dali/bus_config.c @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2015-2016, Arkadiusz Materek (arekmat@poczta.fm) + * + * Licensed under GNU General Public License 3.0 or later. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "bus_config.h" + +const XMC_UART_CH_CONFIG_t kDaliTxUARTConfig = { + baudrate: 2400, + data_bits: 0, + frame_length: 19, + stop_bits: 2, + oversampling: 0, + parity_mode: XMC_USIC_CH_PARITY_MODE_NONE +}; + +const XMC_CCU4_SLICE_EVENT_CONFIG_t kDaliRxCCU4CaptureRisingConfig = { + mapped_input: CCU40_SLICE_INPUT, + edge: XMC_CCU4_SLICE_EVENT_EDGE_SENSITIVITY_RISING_EDGE, + level: XMC_CCU4_SLICE_EVENT_LEVEL_SENSITIVITY_ACTIVE_HIGH, + duration: XMC_CCU4_SLICE_EVENT_FILTER_7_CYCLES +}; + +const XMC_CCU4_SLICE_EVENT_CONFIG_t kDaliRxCCU4CaptureFallingConfig = { + mapped_input: CCU40_SLICE_INPUT, + edge: XMC_CCU4_SLICE_EVENT_EDGE_SENSITIVITY_FALLING_EDGE, + level: XMC_CCU4_SLICE_EVENT_LEVEL_SENSITIVITY_ACTIVE_HIGH, + duration: XMC_CCU4_SLICE_EVENT_FILTER_7_CYCLES +}; + +const XMC_CCU4_SLICE_CAPTURE_CONFIG_t kDaliRxCCU4CaptureConfig = { + tc: (1 << CCU4_CC4_TC_CMOD_Pos) | (1 << CCU4_CC4_TC_CLST_Pos) | (3 << CCU4_CC4_TC_CAPC_Pos) | (1 << CCU4_CC4_TC_CCS_Pos) | (1 << CCU4_CC4_TC_TSSM_Pos), + prescaler_initval: 0, + float_limit: 0, + timer_concatenation: 0 +}; diff --git a/src/xmc1200/dali/bus_config.h b/src/xmc1200/dali/bus_config.h new file mode 100644 index 0000000..aa52e82 --- /dev/null +++ b/src/xmc1200/dali/bus_config.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2015-2016, Arkadiusz Materek (arekmat@poczta.fm) + * + * Licensed under GNU General Public License 3.0 or later. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef XMC_DALI_BUS_CONFIG_H_ +#define XMC_DALI_BUS_CONFIG_H_ + +#include +#include +#include + +// UART configuration - used for DALI TX +# define DALI_UART_CH XMC_USIC0_CH0 +# define DALI_UART_TX_PIN_MODE XMC_GPIO_MODE_OUTPUT_PUSH_PULL_ALT2 +# define DALI_UART_TX_PIN P1_5 + +extern const XMC_UART_CH_CONFIG_t kDaliTxUARTConfig; + +// CCU4 configuration - used for DALI TX +# define CCU40_SLICE CCU40_CC42 +# define CCU40_SLICE_NUMBER 2 +# define CCU40_SLICE_INPUT XMC_CCU4_SLICE_INPUT_C +# define CCU40_SLICE_SHADDOW_TRANSFER XMC_CCU4_SHADOW_TRANSFER_SLICE_2 +# define CCU40_RX_PIN P0_2 +# define DALI_XMC_CCU40_RX_PIN_MODE XMC_GPIO_MODE_INPUT_PULL_DOWN + +extern const XMC_CCU4_SLICE_EVENT_CONFIG_t kDaliRxCCU4CaptureRisingConfig; +extern const XMC_CCU4_SLICE_EVENT_CONFIG_t kDaliRxCCU4CaptureFallingConfig; +extern const XMC_CCU4_SLICE_CAPTURE_CONFIG_t kDaliRxCCU4CaptureConfig; + +#endif // XMC_DALI_BUS_CONFIG_H_ diff --git a/src/xmc1200/dali/lamp.cpp b/src/xmc1200/dali/lamp.cpp new file mode 100644 index 0000000..9c3e04b --- /dev/null +++ b/src/xmc1200/dali/lamp.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2015-2016, Arkadiusz Materek (arekmat@poczta.fm) + * + * Licensed under GNU General Public License 3.0 or later. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "lamp.hpp" + +#include + +using namespace xmc; + +namespace dali { +namespace xmc { + +// LED1 +#define LED1_RED Bccu::CH0 +#define LED1_GREEN Bccu::CH7 +#define LED1_BLUE Bccu::CH8 + +#define LED1_ENGINE Bccu::DE0 + +// LED2 +#define LED2_RED Bccu::CH1 +#define LED2_GREEN Bccu::CH2 +#define LED2_BLUE Bccu::CH3 + +#define LED2_ENGINE Bccu::DE1 + +// LED3 +#define LED3_RED Bccu::CH4 +#define LED3_GREEN Bccu::CH5 +#define LED3_BLUE Bccu::CH6 + +#define LED3_ENGINE Bccu::DE2 + +//static +#define DRIVER_MAX 4096 +#define DALI_MAX 65536 + +inline uint16_t dali2driver(uint16_t level) { + uint32_t result = ((uint32_t) level * DRIVER_MAX + DALI_MAX / 2) / DALI_MAX; + return result < DRIVER_MAX ? result : DRIVER_MAX - 1; +} + +inline uint16_t driver2dali(uint16_t intensivity) { + uint32_t result = ((uint32_t) intensivity * DALI_MAX + DRIVER_MAX / 2) / DRIVER_MAX; + return result < DALI_MAX ? result : DALI_MAX - 1; +} + +//static +LampRGB* LampRGB::getInstance() { + static LampRGB gDaliLamp1(LED1_ENGINE, LED1_RED, LED1_GREEN, LED1_BLUE, &kLampBCCUChannelConfig); + return &gDaliLamp1; +} + +LampRGB::LampRGB(Bccu::DimmingEngine de, Bccu::Channel r, Bccu::Channel g, Bccu::Channel b, const XMC_BCCU_CH_CONFIG_t* channelConfig) : + mLamp(de, r, g, b, channelConfig, channelConfig, channelConfig), mLevel(0) { + memset(mClients, 0, sizeof(mClients)); + mLamp.enable(); + mLamp.setColor(DRIVER_MAX - 1, DRIVER_MAX - 1, DRIVER_MAX - 1, 0); +} + +LampRGB::~LampRGB() { + mLamp.disable(); +} + +Status LampRGB::registerClient(ILampClient* c) { + for (uint16_t i = 0; i < kMaxClients; ++i) { + if (mClients[i] == nullptr) { + mClients[i] = c; + return Status::OK; + } + } + return Status::ERROR; +} + +Status LampRGB::unregisterClient(ILampClient* c) { + for (uint16_t i = 0; i < kMaxClients; ++i) { + if (mClients[i] == c) { + mClients[i] = nullptr; + return Status::OK; + } + } + return Status::ERROR; +} + +void LampRGB::setLevel(uint16_t level, uint32_t fadeTime) { + mLevel = level; + mLamp.setLevel(dali2driver(level), fadeTime); +} + +uint16_t LampRGB::getLevel() { + if (mLamp.isFading()) { + return driver2dali(mLamp.getLevel()); + } + return mLevel; +} + +bool LampRGB::isFading() { + return mLamp.isFading(); +} + +void LampRGB::abortFading() { + if (mLamp.isFading()) { + mLamp.abortFading(); + mLevel = driver2dali(mLamp.getLevel()); + } +} + +void LampRGB::waitForFade() { + while (mLamp.isFading()) { + } +} + +void LampRGB::onLampStateChnaged(ILampState state) { + for (uint16_t i = 0; i < kMaxClients; ++i) { + if (mClients[i] != nullptr) { + mClients[i]->onLampStateChnaged(state); + } + } +} + +} // namespace xmc +} // namespace dali diff --git a/src/xmc1200/dali/lamp.hpp b/src/xmc1200/dali/lamp.hpp new file mode 100644 index 0000000..bd134c2 --- /dev/null +++ b/src/xmc1200/dali/lamp.hpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2015-2016, Arkadiusz Materek (arekmat@poczta.fm) + * + * Licensed under GNU General Public License 3.0 or later. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef XMC_DALI_LAMP_HPP_ +#define XMC_DALI_LAMP_HPP_ + +#include +#include + +namespace dali { +namespace xmc { + +class LampRGB: public dali::ILamp { +public: + + static LampRGB* getInstance(); + + dali::Status registerClient(ILampClient* c) override; + dali::Status unregisterClient(ILampClient* c) override; + void setLevel(uint16_t level, uint32_t fadeTime) override; + uint16_t getLevel() override; + bool isFading() override; + void abortFading() override; + void waitForFade(); + + bool isOff() { + return getLevel() == 0; + } + +private: + LampRGB(::xmc::Bccu::DimmingEngine de, ::xmc::Bccu::Channel r, ::xmc::Bccu::Channel g, ::xmc::Bccu::Channel b, const XMC_BCCU_CH_CONFIG_t* channelConfig); + LampRGB(const LampRGB&) = delete; + LampRGB& operator=(const LampRGB&) = delete; + + ~LampRGB(); + + void onLampStateChnaged(ILampState state); + + static const uint8_t kMaxClients = 1; + + ILampClient* mClients[kMaxClients]; + ::xmc::BccuLampRGB mLamp; + uint16_t mLevel; +}; + + +} // namespace xmc +} // namespace dali + +#endif // XMC_DALI_LAMP_HPP_ diff --git a/src/xmc1200/dali/memory.cpp b/src/xmc1200/dali/memory.cpp new file mode 100644 index 0000000..a7d1678 --- /dev/null +++ b/src/xmc1200/dali/memory.cpp @@ -0,0 +1,392 @@ +/* + * Copyright (c) 2015-2016, Arkadiusz Materek (arekmat@poczta.fm) + * + * Licensed under GNU General Public License 3.0 or later. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "memory.hpp" + +#include "memory_config.hpp" +#include "timer.hpp" + +namespace dali { +namespace xmc { +namespace { + +#define FLASH_MEMORY_OFFSET 4 +#define FLASH_MEMORY_SIZE (XMC_FLASH_BYTES_PER_PAGE - FLASH_MEMORY_OFFSET) + +class FlashMemory { +public: + FlashMemory(uint32_t* pageAddrA, uint32_t* pageAddrB, size_t words) : + mPageAddrA(pageAddrA), mPageAddrB(pageAddrB), mWords(words + 1), mState(State::UINTIALIZED) { + mData = new uint32_t[mWords]; + } + + ~FlashMemory() { + delete mData; + } + + size_t write(uintptr_t addr, const uint8_t* data, size_t size) { + if (mState == State::UINTIALIZED) { + return 0; + } + FlashMetaData* metaData = (FlashMetaData*) mData; + uint8_t* writeData = (uint8_t*) mData + addr + FLASH_MEMORY_OFFSET; + bool changed = false; + for (size_t i = 0; i < size; ++i, ++writeData, ++data) { + if (*writeData != *data) { + changed = true; + } + // update checksum + metaData->crc16 += *writeData; + metaData->crc16 -= *data; + // store data + *writeData = *data; + } + if (changed) { + switch (mState) { + case State::SYNCHORONIZED_A: + case State::DATA_MODIFIED_A: + mState = State::DATA_MODIFIED_A; + break; + + case State::SYNCHORONIZED_B: + case State::DATA_MODIFIED_B: + mState = State::DATA_MODIFIED_B; + break; + + default: + break; + } + } + return size; + } + + size_t read(uintptr_t addr, uint8_t* data, size_t size) { + uint8_t* readData = (uint8_t*) mData + addr + FLASH_MEMORY_OFFSET; + memcpy(data, (uint8_t*) readData, size); + return size; + } + + const uint8_t* getData(uintptr_t addr) { + return (uint8_t*) mData + addr + FLASH_MEMORY_OFFSET; + } + + void erase() { + erasePage(mPageAddrA); + erasePage(mPageAddrB); + } + + void synchronize(bool emergency) { + switch (mState) { + case State::SYNCHORONIZED_A: + case State::SYNCHORONIZED_B: + // do nothing + break; + + case State::DATA_MODIFIED_A: { + const FlashMetaData* metaDataB = (FlashMetaData*) mPageAddrB; + if (metaDataB->metaData == 0xffffffff) { // fast check erased + synchronizePages(mPageAddrB, emergency ? nullptr : mPageAddrA); + mState = State::SYNCHORONIZED_B; + } else { + const FlashMetaData* metaDataA = (FlashMetaData*) mPageAddrA; + if (metaDataA->metaData == 0xffffffff) { // fast check erased + synchronizePages(mPageAddrA, emergency ? nullptr : mPageAddrB); + mState = State::SYNCHORONIZED_A; + } else { + // retry next time + erasePage(mPageAddrB); + } + } + break; + } + case State::DATA_MODIFIED_B: { + const FlashMetaData* metaDataA = (FlashMetaData*) mPageAddrA; + if (metaDataA->metaData == 0xffffffff) { // fast check erased + synchronizePages(mPageAddrA, emergency ? nullptr : mPageAddrB); + mState = State::SYNCHORONIZED_A; + } else { + const FlashMetaData* metaDataB = (FlashMetaData*) mPageAddrB; + if (metaDataB->metaData == 0xffffffff) { // fast check erased + synchronizePages(mPageAddrB, emergency ? nullptr : mPageAddrA); + mState = State::SYNCHORONIZED_B; + } else { + // retry next time + erasePage(mPageAddrA); + } + } + break; + } + case State::UINTIALIZED: + default: { + initialize(); + break; + } + } // switch (mState) + } + +private: + + enum class ReadState { + INVALID, ERASED, OK + }; + + enum class State { + UINTIALIZED, SYNCHORONIZED_A, SYNCHORONIZED_B, DATA_MODIFIED_A, DATA_MODIFIED_B, + }; + + typedef union { + struct { + uint32_t crc16 :16; + uint32_t index :16; + }; + uint32_t metaData; + } FlashMetaData; + + void writePage(uint32_t* flash) { + const size_t blocks = mWords / (XMC_FLASH_WORDS_PER_PAGE / XMC_FLASH_BLOCKS_PER_PAGE); + uint32_t* data = mData; + FlashMetaData* metaDataA = (FlashMetaData*) data; + metaDataA->index--; + for (uint16_t j = 0; j < blocks; j++) { + __disable_irq(); + + NVM->NVMPROG &= (uint16_t) (~(uint16_t) NVM_NVMPROG_ACTION_Msk); + NVM->NVMPROG |= (uint16_t) (NVM_NVMPROG_RSTVERR_Msk | NVM_NVMPROG_RSTECC_Msk); + NVM->NVMPROG |= (uint16_t) ((uint32_t) 0xa1 << NVM_NVMPROG_ACTION_Pos); + + for (uint16_t i = 0; i < XMC_FLASH_WORDS_PER_BLOCK; ++i, ++data, ++flash) { + *flash = *data; + } + + while (XMC_FLASH_IsBusy() == true) { + } + + NVM->NVMPROG &= (uint16_t) (~(uint16_t) NVM_NVMPROG_ACTION_Msk); + + __enable_irq(); + } + } + + bool verifyPage(uint32_t* flash) { + uint32_t* data = mData; + for (uint16_t i = 0; i < mWords; ++i, ++data, ++flash) { + if (*flash != *data) { + return false; + } + } + return true; + } + + ReadState readPage(uint32_t* flash, uint32_t* data) { + const FlashMetaData* metaData = (FlashMetaData*) flash; + uint16_t crc16 = metaData->crc16; + *data++ = *flash++; + ReadState state = ReadState::ERASED; + for (uint16_t i = 1; i < mWords; ++i, ++data, ++flash) { + uint32_t word = *flash; + crc16 += (word >> 24) & 0xff; + crc16 += (word >> 16) & 0xff; + crc16 += (word >> 8) & 0xff; + crc16 += (word >> 0) & 0xff; + if (word != 0xffffffff) { + state = ReadState::INVALID; + } + *data = word; + } + if (crc16 == 0) { + state = ReadState::OK; + } + return state; + } + + void erasePage(uint32_t* flash) { + __disable_irq(); + XMC_FLASH_ErasePage(flash); + __enable_irq(); + } + + void initialize() { + uint32_t dataB[mWords]; + + ReadState readStateA = readPage(mPageAddrA, mData); + ReadState readStateB = readPage(mPageAddrB, dataB); + + switch (readStateA) { + case ReadState::ERASED: { + + switch (readStateB) { + case ReadState::ERASED: + mData[0] = (0 - (0xff * (mWords - 1) * sizeof(uint32_t))) | 0xffff0000; + mState = State::SYNCHORONIZED_A; + break; + + case ReadState::INVALID: + erasePage(mPageAddrB); + mData[0] = (0 - (0xff * (mWords - 1) * sizeof(uint32_t))) | 0xffff0000; + mState = State::SYNCHORONIZED_B; + break; + + case ReadState::OK: + memcpy(mData, dataB, mWords * sizeof(uint32_t)); + mData[0] |= 0xffff0000; + mState = State::SYNCHORONIZED_B; + break; + } + + break; + } + + case ReadState::OK: { + + switch (readStateB) { + case ReadState::ERASED: + mData[0] |= 0xffff0000; + mState = State::SYNCHORONIZED_A; + break; + + case ReadState::INVALID: + erasePage(mPageAddrB); + mData[0] |= 0xffff0000; + mState = State::SYNCHORONIZED_A; + break; + + case ReadState::OK: { + FlashMetaData* metaDataA = (FlashMetaData*) mData; + FlashMetaData* metaDataB = (FlashMetaData*) dataB; + if (metaDataA->index < metaDataB->index) { + erasePage(mPageAddrB); + mState = State::SYNCHORONIZED_A; + } else { + erasePage(mPageAddrA); + memcpy(mData, dataB, mWords * sizeof(uint32_t)); + mState = State::SYNCHORONIZED_B; + } + + break; + } + } + + break; + } + + case ReadState::INVALID: { + erasePage(mPageAddrA); + + switch (readStateB) { + case ReadState::ERASED: + mData[0] = (0 - (0xff * (mWords - 1) * sizeof(uint32_t))) | 0xffff0000; + memset(&mData[1], 0xff, mWords * sizeof(uint32_t)); + mState = State::SYNCHORONIZED_A; + break; + + case ReadState::INVALID: + erasePage(mPageAddrB); + + mData[0] = (0 - (0xff * (mWords - 1) * sizeof(uint32_t))) | 0xffff0000; + memset(&mData[1], 0xff, mWords * sizeof(uint32_t)); + mState = State::SYNCHORONIZED_A; + break; + + case ReadState::OK: + memcpy(mData, dataB, mWords * sizeof(uint32_t)); + mData[0] |= 0xffff0000; + mState = State::SYNCHORONIZED_B; + break; + } + + break; + } + + } // switch (readStateA) + } + + void synchronizePages(uint32_t* writePageAddr, uint32_t* erasePageAddr) { + writePage(writePageAddr); + if (verifyPage(writePageAddr)) { + if (erasePageAddr != nullptr) { + erasePage(erasePageAddr); + } + } + } + + uint32_t* const mPageAddrA; + uint32_t* const mPageAddrB; + const size_t mWords; + uint32_t* mData; + State mState; +}; + +#define TEMP_SIZE 32 + +FlashMemory gDataMemory((uint32_t*) (XMC_DALI_FLASH_START + XMC_DALI_FLASH_SIZE - XMC_FLASH_BYTES_PER_PAGE * 4), + (uint32_t*) (XMC_DALI_FLASH_START + XMC_DALI_FLASH_SIZE - XMC_FLASH_BYTES_PER_PAGE * 3), + FLASH_MEMORY_SIZE / sizeof(uint32_t)); +} // namespace + +//static +Memory* Memory::getInstance() { + static Memory gMemory1(&gDataMemory, FLASH_MEMORY_SIZE - TEMP_SIZE, FLASH_MEMORY_SIZE - TEMP_SIZE); + return &gMemory1; +} + +Memory::Memory(void* handle, size_t dataSize, uintptr_t tempAddr) : + mHandle(handle), mTempAddr(tempAddr) { + ((FlashMemory*) handle)->synchronize(false); +} + +size_t Memory::dataSize() { + return FLASH_MEMORY_SIZE; +} + +size_t Memory::dataWrite(uintptr_t addr, const uint8_t* data, size_t size) { + if (addr + size > FLASH_MEMORY_SIZE) { + return 0; + } + return ((FlashMemory*) mHandle)->write(addr, data, size); +} + +const uint8_t* Memory::data(uintptr_t addr, size_t size) { + if (addr + size > FLASH_MEMORY_SIZE) { + return nullptr; + } + return ((FlashMemory*) mHandle)->getData(addr); +} + +size_t Memory::tempSize() { + return TEMP_SIZE; +} + +size_t Memory::tempWrite(uintptr_t addr, const uint8_t* data, size_t size) { + if (addr + size > TEMP_SIZE) { + return 0; + } + return ((FlashMemory*) mHandle)->write(mTempAddr + addr, data, size); +} + +const uint8_t* Memory::tempData(uintptr_t addr, size_t size) { + if (addr + size > TEMP_SIZE) { + return nullptr; + } + return ((FlashMemory*) mHandle)->getData(mTempAddr + addr); +} + +//static +void Memory::synchronize(bool emergency) { + gDataMemory.synchronize(emergency); +} + +//static +void Memory::erase(bool temp) { + gDataMemory.erase(); +} + +} +// namespace xmc +}// namespace dali diff --git a/src/xmc1200/dali/memory.hpp b/src/xmc1200/dali/memory.hpp new file mode 100644 index 0000000..8f3f1de --- /dev/null +++ b/src/xmc1200/dali/memory.hpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2015-2016, Arkadiusz Materek (arekmat@poczta.fm) + * + * Licensed under GNU General Public License 3.0 or later. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef XMC_DALI_MEMORY_H_ +#define XMC_DALI_MEMORY_H_ + +#include + +#include +#include + +namespace dali { +namespace xmc { + +class Memory: public dali::IMemory { +public: + static Memory* getInstance(); + + size_t dataSize() override; + size_t dataWrite(uintptr_t addr, const uint8_t* data, size_t size) override; + const uint8_t* data(uintptr_t addr, size_t size) override; + + size_t tempSize() override; + size_t tempWrite(uintptr_t addr, const uint8_t* data, size_t size) override; + const uint8_t* tempData(uintptr_t addr, size_t size) override; + + static void synchronize(bool temp); + static void erase(bool temp); + +private: + Memory(void* handle, size_t dataSize, uintptr_t tempAddr); + Memory(const Memory& other) = delete; + Memory& operator=(const Memory&) = delete; + + const void* mHandle; + const uintptr_t mTempAddr; +}; + +} // namespace xmc +} // namespace dali + +#endif // XMC_DALI_MEMORY_H_ diff --git a/src/xmc1200/dali/memory_config.hpp b/src/xmc1200/dali/memory_config.hpp new file mode 100644 index 0000000..20e588d --- /dev/null +++ b/src/xmc1200/dali/memory_config.hpp @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2015-2016, Arkadiusz Materek (arekmat@poczta.fm) + * + * Licensed under GNU General Public License 3.0 or later. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef XMC_DALI_MEMORY_CONFIG_H_ +#define XMC_DALI_MEMORY_CONFIG_H_ + +# define XMC_DALI_FLASH_START 0x10001000 +# define XMC_DALI_FLASH_SIZE 0x32000 + +#endif // XMC_DALI_MEMORY_CONFIG_H_ diff --git a/src/xmc1200/dali/timer.cpp b/src/xmc1200/dali/timer.cpp new file mode 100644 index 0000000..bc4221b --- /dev/null +++ b/src/xmc1200/dali/timer.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2015-2016, Arkadiusz Materek (arekmat@poczta.fm) + * + * Licensed under GNU General Public License 3.0 or later. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "timer.hpp" + +#include + +namespace dali { +namespace xmc { + +namespace { + +const uint16_t* kUniqeChipId = (uint16_t*) 0x10000FF0; // 8 elements + +#define MAX_TASKS (3) +#define TICKS_PER_SECOND 1000 + +typedef struct { + dali::ITimer::ITimerTask* task; + uint64_t time; + uint32_t period; +} TaskInfo; + +TaskInfo gTasks[MAX_TASKS]; +volatile uint64_t gSystemTimeMs; + +} + +//static +Timer* Timer::getInstance() { + static Timer gDaliTimer; + return &gDaliTimer; +} + +Timer::Timer() { + XMC_PRNG_INIT_t prngConfig; + prngConfig.key_words[0] = kUniqeChipId[3]; + prngConfig.key_words[1] = kUniqeChipId[4]; + prngConfig.key_words[2] = kUniqeChipId[5]; + prngConfig.key_words[3] = kUniqeChipId[6]; + prngConfig.key_words[4] = kUniqeChipId[7]; + prngConfig.block_size = XMC_PRNG_RDBS_WORD; + XMC_PRNG_Init(&prngConfig); + + SysTick_Config(SystemCoreClock / TICKS_PER_SECOND); +} + +Timer::~Timer() { + WR_REG(SysTick->CTRL, SysTick_CTRL_ENABLE_Msk, SysTick_CTRL_ENABLE_Pos, 0); + + XMC_PRNG_DeInit(); +} + +dali::Status Timer::schedule(ITimerTask* task, uint32_t delay, uint32_t period) { + for (uint8_t i = 0; i < MAX_TASKS; ++i) { + TaskInfo* taskInfo = &gTasks[i]; + if (taskInfo->task == nullptr) { + taskInfo->task = task; + taskInfo->time = gSystemTimeMs + delay; + taskInfo->period = period; + return dali::Status::OK; + } + } + return dali::Status::ERROR; +} + +void Timer::cancel(ITimerTask* task) { + for (uint8_t i = 0; i < MAX_TASKS; ++i) { + TaskInfo* taskInfo = &gTasks[i]; + if (taskInfo->task == task) { + taskInfo->task = nullptr; + } + } +} + +uint32_t Timer::randomize() { + return ((uint32_t) XMC_PRNG_GetPseudoRandomNumber() << 8) + (uint32_t) gSystemTimeMs; +} + +// static +uint64_t Timer::getTimeMs() { + return gSystemTimeMs; +} + +// static +uint16_t Timer::freq() { + return TICKS_PER_SECOND; +} + +// static +void Timer::runSlice() { + for (uint8_t i = 0; i < MAX_TASKS; ++i) { + TaskInfo* taskInfo = &gTasks[i]; + + if (taskInfo->task != nullptr) { + if (taskInfo->time <= gSystemTimeMs) { + taskInfo->task->timerTaskRun(); + if (taskInfo->period != 0) { + taskInfo->time += taskInfo->period; + } else { + taskInfo->task = nullptr; + } + } + } + } +} + +extern "C" { + +void SysTick_Handler(void) { + gSystemTimeMs += 1000 / TICKS_PER_SECOND; +} + +} // extern "C" + +} // namespace xmc +} // namespace dali diff --git a/src/xmc1200/dali/timer.hpp b/src/xmc1200/dali/timer.hpp new file mode 100644 index 0000000..aeea90c --- /dev/null +++ b/src/xmc1200/dali/timer.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2015-2016, Arkadiusz Materek (arekmat@poczta.fm) + * + * Licensed under GNU General Public License 3.0 or later. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef XMC_DALI_TIMER_H_ +#define XMC_DALI_TIMER_H_ + +#include + +namespace dali { +namespace xmc { + +class Timer: public dali::ITimer { +public: + static Timer* getInstance(); + + uint64_t getTime() override { + return getTimeMs(); + } + dali::Status schedule(ITimerTask* task, uint32_t delay, uint32_t period) override; + void cancel(ITimerTask* task) override; + uint32_t randomize() override; + + static uint64_t getTimeMs(); + static uint16_t freq(); + static void runSlice(); + +private: + Timer(); + Timer(const Timer& other) = delete; + Timer& operator=(const Timer&) = delete; + + ~Timer(); +}; + +} // namespace xmc +} // namespace dali + +#endif // XMC_DALI_TIMER_H_ diff --git a/src/xmc1200/main.cpp b/src/xmc1200/main.cpp new file mode 100644 index 0000000..834849d --- /dev/null +++ b/src/xmc1200/main.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2015-2016, Arkadiusz Materek (arekmat@poczta.fm) + * + * Licensed under GNU General Public License 3.0 or later. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#if (CPU_CLOCK == 32000000) +#define XMC_CPU_FREQ xmc::Clock::FREQ_32MHZ +#elif (CPU_CLOCK == 16000000) +#define XMC_CPU_FREQ xmc::Clock::FREQ_16MHZ +#else +#error Unsupported CPU clock +#endif + +#include + +#include +#include +#include +#include +#include + +#include +#include + +dali::Slave* gSlave; + +void waitForInterrupt() { + __WFI(); +} + +void initPowerDetector() { + XMC_SCU_SUPPLYMONITOR_t config; + config.ext_supply_threshold = 0b10; + config.ext_supply_monitor_speed = 0b00; + config.enable_prewarning_int = true; + config.enable_vdrop_int = false; + config.enable_vclip_int = false; + config.enable_at_init = true; + + XMC_SCU_SupplyMonitorInit(&config); + NVIC_EnableIRQ(SCU_1_IRQn); +} + +void onPowerUp() { + gSlave->notifyPowerUp(); +} + +void onPowerDown() { + gSlave->notifyPowerDown(); + dali::xmc::Memory::synchronize(true); +} + +class PowerOnTimerTask: public dali::ITimer::ITimerTask { +public: + + void timerTaskRun() override { + onPowerUp(); + } +}; + +PowerOnTimerTask gPowerOnTimerTask; + +volatile bool gEmergencyMemorySynchronise; + +int main(void) { + xmc::Clock::init(XMC_CPU_FREQ); + + + initPowerDetector(); + + dali::xmc::Timer* daliTimer = dali::xmc::Timer::getInstance(); + dali::xmc::Bus* daliBus = dali::xmc::Bus::getInstance(); + + dali::xmc::Memory* daliMemory1 = dali::xmc::Memory::getInstance(); + dali::xmc::LampRGB* daliLamp1 = dali::xmc::LampRGB::getInstance(); + gSlave = dali::Slave::create(daliMemory1, daliLamp1, daliBus, daliTimer); + + daliTimer->schedule(&gPowerOnTimerTask, 600, 0); + + XMC_GPIO_SetMode(XMC_GPIO_PORT0, 0, XMC_GPIO_MODE_OUTPUT_PUSH_PULL); + XMC_GPIO_SetOutputHigh(XMC_GPIO_PORT0, 0); + + while (true) { + waitForInterrupt(); // Interrupts are triggered by SysTick 1kHz and from CCU4 (DALI RX) up to 2,4Khz + if (gEmergencyMemorySynchronise) { + gEmergencyMemorySynchronise = false; + onPowerDown(); + } + dali::xmc::Timer::runSlice(); + dali::xmc::Bus::runSlice(); + } + return 0; +} + +extern "C" { + +void HardFault_Handler(void) { + XMC_SCU_RESET_AssertMasterReset(); +} + +void SCU_1_IRQHandler(void) { + if (SCU_INTERRUPT->SRRAW & SCU_INTERRUPT_SRRAW_VDDPI_Msk) { + SCU_INTERRUPT->SRCLR = SCU_INTERRUPT_SRCLR_VDDPI_Msk; + gEmergencyMemorySynchronise = true; + } +} + +} // extern "C" + diff --git a/src/xmc1200/startup_XMC1200.S b/src/xmc1200/startup_XMC1200.S new file mode 100644 index 0000000..2f776cf --- /dev/null +++ b/src/xmc1200/startup_XMC1200.S @@ -0,0 +1,485 @@ +/****************************************************************************** + * @file startup_XMC1200.s + * @brief CMSIS Core Device Startup File for + * Infineon XMC1200 Device Series + * @version V1.13 + * @date Dec 2014 + * + * Copyright (C) 2014 Infineon Technologies AG. All rights reserved. + * + * + * @par + * Infineon Technologies AG (Infineon) is supplying this software for use with + * Infineon's microcontrollers. This file can be freely distributed + * within development tools that are supporting such microcontrollers. + * + * @par + * THIS SOFTWARE IS PROVIDED AS IS. NO WARRANTIES, WHETHER EXPRESS, IMPLIED + * OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. + * ARM SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR + * CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER. + * + ******************************************************************************/ + +/********************** Version History *************************************** + * V1.0, Oct, 02, 2012 PKB:Startup file for XMC1 + * V1.1, Oct, 19, 2012 PKB:ERU and MATH interrupt handlers + * V1.2, Nov, 02, 2012 PKB:Renamed AllowPLLInitByStartup to AllowClkInitByStartup + * V1.3, Dec, 11, 2012 PKB:Attributes of .XmcVeneerCode section changed + * V1.4, Dec, 13, 2012 PKB:Removed unwanted interrupts/veneers + * V1.5, Jan, 26, 2013 PKB:Corrected the SSW related entries + * V1.6, Feb, 13, 2013 PKB:Relative path to Device_Data.h + * V1.7, Feb, 19, 2013 PKB:Included XMC1100_SCU.inc + * V1.8, Jan, 24, 2014 PKB:Removed AllowClkInitStartup and DAVE Extended init + * V1.9, Feb, 05, 2014 PKB:Removed redundant alignment code from copy+clear funcs + * V1.10, Feb, 14, 2014 PKB:Added software_init_hook and hardware_init_hook + * V1.11, May, 06, 2014 JFT:__COPY_FLASH2RAM to initialize ram + * Added ram_code section initialization + * V1.12, Sep, 29, 2014 JFT:One single default handler + * Device_Data.h not included, user may use CLKVAL1_SSW + * and CLKVAL2_SSW. + * software_init_hook and hardware_init_hook removed + * Misc optimizations + * V1.13, Dec, 11,2014 JFT:Default clocking changed, MCLK=32MHz and PCLK=64MHz + ******************************************************************************/ + +/***************************************************************************** + * Clock system handling by SSW + * CLK_VAL1 Configuration + * FDIV Fractional Divider Selection + * IDIV Divider Selection (limited to 1-16) + * <0=> Divider is bypassed + * <1=> MCLK = 32 MHz + * <2=> MCLK = 16 MHz + * <3=> MCLK = 10.67 MHz + * <4=> MCLK = 8 MHz + * <254=> MCLK = 126 kHz + * <255=> MCLK = 125.5 kHz + * PCLKSEL PCLK Clock Select + * <0=> PCLK = MCLK + * <1=> PCLK = 2 x MCLK + * RTCCLKSEL RTC Clock Select + * <0=> 32.768kHz standby clock + * <1=> 32.768kHz external clock from ERU0.IOUT0 + * <2=> 32.768kHz external clock from ACMP0.OUT + * <3=> 32.768kHz external clock from ACMP1.OUT + * <4=> 32.768kHz external clock from ACMP2.OUT + * <5=> Reserved + * <6=> Reserved + * <7=> Reserved + * do not move CLK_VAL1 to SCU_CLKCR[0..19] + * + *****************************************************************************/ +#define CLKVAL1_SSW 0x00010100 + +/***************************************************************************** + * CLK_VAL2 Configuration + * disable VADC and SHS Gating + * disable CCU80 Gating + * disable CCU40 Gating + * disable USIC0 Gating + * disable BCCU0 Gating + * disable LEDTS0 Gating + * disable LEDTS1 Gating + * disable POSIF0 Gating + * disable MATH Gating + * disable WDT Gating + * disable RTC Gating + * do not move CLK_VAL2 to SCU_CGATCLR0[0..10] + * + *****************************************************************************/ +#define CLKVAL2_SSW 0x80000000 + +/* A macro to define vector table entries */ +.macro Entry Handler + .long \Handler +.endm + +/* A couple of macros to ease definition of the various handlers */ +.macro Insert_ExceptionHandler Handler_Func + .weak \Handler_Func + .thumb_set \Handler_Func, Default_handler +.endm + +/* ================== START OF VECTOR TABLE DEFINITION ====================== */ +/* Vector Table - This is indirectly branched to through the veneers */ + .syntax unified + .cpu cortex-m0 + + .section ".reset" + + .align 2 + + .globl __Vectors + .type __Vectors, %object +__Vectors: + .long __initial_sp /* Top of Stack */ + .long Reset_Handler /* Reset Handler */ +/* + * All entries below are redundant for M0, but are retained because they can + * in the future be directly ported to M0 Plus devices. + */ + .long 0 /* Reserved */ + Entry HardFault_Handler /* Hard Fault Handler */ + .long CLKVAL1_SSW /* Reserved */ + .long CLKVAL2_SSW /* Reserved */ +#ifdef RETAIN_VECTOR_TABLE + .long 0 /* Reserved */ + .long 0 /* Reserved */ + .long 0 /* Reserved */ + .long 0 /* Reserved */ + .long 0 /* Reserved */ + Entry SVC_Handler /* SVCall Handler */ + .long 0 /* Reserved */ + .long 0 /* Reserved */ + Entry PendSV_Handler /* PendSV Handler */ + Entry SysTick_Handler /* SysTick Handler */ + + /* Interrupt Handlers for Service Requests (SR) from XMC1200 Peripherals */ + Entry SCU_0_IRQHandler /* Handler name for SR SCU_0 */ + Entry SCU_1_IRQHandler /* Handler name for SR SCU_1 */ + Entry SCU_2_IRQHandler /* Handler name for SR SCU_2 */ + Entry ERU0_0_IRQHandler /* Handler name for SR ERU0_0 */ + Entry ERU0_1_IRQHandler /* Handler name for SR ERU0_1 */ + Entry ERU0_2_IRQHandler /* Handler name for SR ERU0_2 */ + Entry ERU0_3_IRQHandler /* Handler name for SR ERU0_3 */ + .long 0 /* Not Available */ + .long 0 /* Not Available */ + Entry USIC0_0_IRQHandler /* Handler name for SR USIC0_0 */ + Entry USIC0_1_IRQHandler /* Handler name for SR USIC0_1 */ + Entry USIC0_2_IRQHandler /* Handler name for SR USIC0_2 */ + Entry USIC0_3_IRQHandler /* Handler name for SR USIC0_3 */ + Entry USIC0_4_IRQHandler /* Handler name for SR USIC0_4 */ + Entry USIC0_5_IRQHandler /* Handler name for SR USIC0_5 */ + Entry VADC0_C0_0_IRQHandler /* Handler name for SR VADC0_C0_0 */ + Entry VADC0_C0_1_IRQHandler /* Handler name for SR VADC0_C0_1 */ + Entry VADC0_G0_0_IRQHandler /* Handler name for SR VADC0_G0_0 */ + Entry VADC0_G0_1_IRQHandler /* Handler name for SR VADC0_G0_1 */ + Entry VADC0_G1_0_IRQHandler /* Handler name for SR VADC0_G1_0 */ + Entry VADC0_G1_1_IRQHandler /* Handler name for SR VADC0_G1_1 */ + Entry CCU40_0_IRQHandler /* Handler name for SR CCU40_0 */ + Entry CCU40_1_IRQHandler /* Handler name for SR CCU40_1 */ + Entry CCU40_2_IRQHandler /* Handler name for SR CCU40_2 */ + Entry CCU40_3_IRQHandler /* Handler name for SR CCU40_3 */ + .long 0 /* Not Available */ + .long 0 /* Not Available */ + .long 0 /* Not Available */ + .long 0 /* Not Available */ + Entry LEDTS0_0_IRQHandler /* Handler name for SR LEDTS0_0 */ + Entry LEDTS1_0_IRQHandler /* Handler name for SR LEDTS1_0 */ + Entry BCCU0_0_IRQHandler /* Handler name for SR BCCU0_0 */ +#endif + + .size __Vectors, . - __Vectors +/* ================== END OF VECTOR TABLE DEFINITION ======================= */ + +/* ================== START OF VECTOR ROUTINES ============================= */ + + .thumb + .align 1 + +/* Reset Handler */ + .thumb_func + .globl Reset_Handler + .type Reset_Handler, %function +Reset_Handler: +/* Initialize interrupt veneer */ + ldr r1, =eROData + ldr r2, =VeneerStart + ldr r3, =VeneerEnd + bl __copy_data + + ldr r0, =SystemInit + blx r0 + +/* Initialize data */ + ldr r1, =DataLoadAddr + ldr r2, =__data_start + ldr r3, =__data_end + bl __copy_data + +/* RAM code */ + ldr r1, =__ram_code_load + ldr r2, =__ram_code_start + ldr r3, =__ram_code_end + bl __copy_data + +/* Define __SKIP_BSS_CLEAR to disable zeroing uninitialzed data in startup. + * The BSS section is specified by following symbols + * __bss_start__: start of the BSS section. + * __bss_end__: end of the BSS section. + * + * Both addresses must be aligned to 4 bytes boundary. + */ +#ifndef __SKIP_BSS_CLEAR + ldr r1, =__bss_start + ldr r2, =__bss_end + + movs r0, 0 + + subs r2, r1 + ble .L_loop3_done + +.L_loop3: + subs r2, #4 + str r0, [r1, r2] + bgt .L_loop3 +.L_loop3_done: +#endif /* __SKIP_BSS_CLEAR */ + +#ifndef __SKIP_LIBC_INIT_ARRAY + ldr r0, =__libc_init_array + blx r0 +#endif + + ldr r0, =main + blx r0 + + .thumb_func + .type __copy_data, %function +__copy_data: +/* The ranges of copy from/to are specified by following symbols + * r1: start of the section to copy from. + * r2: start of the section to copy to + * r3: end of the section to copy to + * + * All addresses must be aligned to 4 bytes boundary. + * Uses r0 + */ + subs r3, r2 + ble .L_loop_done + +.L_loop: + subs r3, #4 + ldr r0, [r1,r3] + str r0, [r2,r3] + bgt .L_loop + +.L_loop_done: + bx lr + + .pool + .size Reset_Handler,.-Reset_Handler +/* ======================================================================== */ +/* ========== START OF EXCEPTION HANDLER DEFINITION ======================== */ + + .align 1 + + .thumb_func + .weak Default_handler + .type Default_handler, %function +Default_handler: + b . + .size Default_handler, . - Default_handler + + Insert_ExceptionHandler HardFault_Handler + Insert_ExceptionHandler SVC_Handler + Insert_ExceptionHandler PendSV_Handler + Insert_ExceptionHandler SysTick_Handler + + Insert_ExceptionHandler SCU_0_IRQHandler + Insert_ExceptionHandler SCU_1_IRQHandler + Insert_ExceptionHandler SCU_2_IRQHandler + Insert_ExceptionHandler ERU0_0_IRQHandler + Insert_ExceptionHandler ERU0_1_IRQHandler + Insert_ExceptionHandler ERU0_2_IRQHandler + Insert_ExceptionHandler ERU0_3_IRQHandler + Insert_ExceptionHandler VADC0_C0_0_IRQHandler + Insert_ExceptionHandler VADC0_C0_1_IRQHandler + Insert_ExceptionHandler VADC0_G0_0_IRQHandler + Insert_ExceptionHandler VADC0_G0_1_IRQHandler + Insert_ExceptionHandler VADC0_G1_0_IRQHandler + Insert_ExceptionHandler VADC0_G1_1_IRQHandler + Insert_ExceptionHandler CCU40_0_IRQHandler + Insert_ExceptionHandler CCU40_1_IRQHandler + Insert_ExceptionHandler CCU40_2_IRQHandler + Insert_ExceptionHandler CCU40_3_IRQHandler + Insert_ExceptionHandler USIC0_0_IRQHandler + Insert_ExceptionHandler USIC0_1_IRQHandler + Insert_ExceptionHandler USIC0_2_IRQHandler + Insert_ExceptionHandler USIC0_3_IRQHandler + Insert_ExceptionHandler USIC0_4_IRQHandler + Insert_ExceptionHandler USIC0_5_IRQHandler + Insert_ExceptionHandler LEDTS0_0_IRQHandler + Insert_ExceptionHandler LEDTS1_0_IRQHandler + Insert_ExceptionHandler BCCU0_0_IRQHandler + +/* ======================================================================== */ + +/* ==================VENEERS VENEERS VENEERS VENEERS VENEERS=============== */ + .section ".XmcVeneerCode","ax",%progbits + + .align 1 + + .globl HardFault_Veneer +HardFault_Veneer: + LDR R0, =HardFault_Handler + MOV PC,R0 + .long 0 + .long 0 + .long 0 + .long 0 + .long 0 + .long 0 + .long 0 +/* ======================================================================== */ + .globl SVC_Veneer +SVC_Veneer: + LDR R0, =SVC_Handler + MOV PC,R0 + .long 0 + .long 0 +/* ======================================================================== */ + .globl PendSV_Veneer +PendSV_Veneer: + LDR R0, =PendSV_Handler + MOV PC,R0 +/* ======================================================================== */ + .globl SysTick_Veneer +SysTick_Veneer: + LDR R0, =SysTick_Handler + MOV PC,R0 +/* ======================================================================== */ + .globl SCU_0_Veneer +SCU_0_Veneer: + LDR R0, =SCU_0_IRQHandler + MOV PC,R0 +/* ======================================================================== */ + .globl SCU_1_Veneer +SCU_1_Veneer: + LDR R0, =SCU_1_IRQHandler + MOV PC,R0 +/* ======================================================================== */ + .globl SCU_2_Veneer +SCU_2_Veneer: + LDR R0, =SCU_2_IRQHandler + MOV PC,R0 +/* ======================================================================== */ + .globl SCU_3_Veneer +SCU_3_Veneer: + LDR R0, =ERU0_0_IRQHandler + MOV PC,R0 +/* ======================================================================== */ + .globl SCU_4_Veneer +SCU_4_Veneer: + LDR R0, =ERU0_1_IRQHandler + MOV PC,R0 +/* ======================================================================== */ + .globl SCU_5_Veneer +SCU_5_Veneer: + LDR R0, =ERU0_2_IRQHandler + MOV PC,R0 +/* ======================================================================== */ + .globl SCU_6_Veneer +SCU_6_Veneer: + LDR R0, =ERU0_3_IRQHandler + MOV PC,R0 + .long 0 + .long 0 +/* ======================================================================== */ + .globl USIC0_0_Veneer +USIC0_0_Veneer: + LDR R0, =USIC0_0_IRQHandler + MOV PC,R0 +/* ======================================================================== */ + .globl USIC0_1_Veneer +USIC0_1_Veneer: + LDR R0, =USIC0_1_IRQHandler + MOV PC,R0 +/* ======================================================================== */ + .globl USIC0_2_Veneer +USIC0_2_Veneer: + LDR R0, =USIC0_2_IRQHandler + MOV PC,R0 +/* ======================================================================== */ + .globl USIC0_3_Veneer +USIC0_3_Veneer: + LDR R0, =USIC0_3_IRQHandler + MOV PC,R0 +/* ======================================================================== */ + .globl USIC0_4_Veneer +USIC0_4_Veneer: + LDR R0, =USIC0_4_IRQHandler + MOV PC,R0 +/* ======================================================================== */ + .globl USIC0_5_Veneer +USIC0_5_Veneer: + LDR R0, =USIC0_5_IRQHandler + MOV PC,R0 +/* ======================================================================== */ + .globl VADC0_C0_0_Veneer +VADC0_C0_0_Veneer: + LDR R0, =VADC0_C0_0_IRQHandler + MOV PC,R0 +/* ======================================================================== */ + .globl VADC0_C0_1_Veneer +VADC0_C0_1_Veneer: + LDR R0, =VADC0_C0_1_IRQHandler + MOV PC,R0 +/* ======================================================================== */ + .globl VADC0_G0_0_Veneer +VADC0_G0_0_Veneer: + LDR R0, =VADC0_G0_0_IRQHandler + MOV PC,R0 +/* ======================================================================== */ + .globl VADC0_G0_1_Veneer +VADC0_G0_1_Veneer: + LDR R0, =VADC0_G0_1_IRQHandler + MOV PC,R0 +/* ======================================================================== */ + .globl VADC0_G1_0_Veneer +VADC0_G1_0_Veneer: + LDR R0, =VADC0_G1_0_IRQHandler + MOV PC,R0 +/* ======================================================================== */ + .globl VADC0_G1_1_Veneer +VADC0_G1_1_Veneer: + LDR R0, =VADC0_G1_1_IRQHandler + MOV PC,R0 +/* ======================================================================== */ + .globl CCU40_0_Veneer +CCU40_0_Veneer: + LDR R0, =CCU40_0_IRQHandler + MOV PC,R0 +/* ======================================================================== */ + .globl CCU40_1_Veneer +CCU40_1_Veneer: + LDR R0, =CCU40_1_IRQHandler + MOV PC,R0 +/* ======================================================================== */ + .globl CCU40_2_Veneer +CCU40_2_Veneer: + LDR R0, =CCU40_2_IRQHandler + MOV PC,R0 +/* ======================================================================== */ + .globl CCU40_3_Veneer +CCU40_3_Veneer: + LDR R0, =CCU40_3_IRQHandler + MOV PC,R0 + .long 0 + .long 0 + .long 0 + .long 0 +/* ======================================================================== */ + .globl LEDTS0_0_Veneer +LEDTS0_0_Veneer: + LDR R0, =LEDTS0_0_IRQHandler + MOV PC,R0 +/* ======================================================================== */ + .globl LEDTS1_0_Veneer +LEDTS1_0_Veneer: + LDR R0, =LEDTS1_0_IRQHandler + MOV PC,R0 +/* ======================================================================== */ + .globl BCCU0_0_Veneer +BCCU0_0_Veneer: + LDR R0, =BCCU0_0_IRQHandler + MOV PC,R0 + +/* ======================================================================== */ +/* ======================================================================== */ + +/* ============= END OF INTERRUPT HANDLER DEFINITION ======================== */ + + .end diff --git a/src/xmc1200/system_XMC1200.c b/src/xmc1200/system_XMC1200.c new file mode 100644 index 0000000..8a7e69c --- /dev/null +++ b/src/xmc1200/system_XMC1200.c @@ -0,0 +1,115 @@ +/****************************************************************************** + * @file system_XMC1200.c + * @brief Device specific initialization for the XMC1200-Series according + * to CMSIS + * @version V1.7 + * @date 11 Dec 2014 + * + * @note + * Copyright (C) 2012-2014 Infineon Technologies AG. All rights reserved. + + * + * @par + * Infineon Technologies AG (Infineon) is supplying this software for use with + * Infineon’s microcontrollers. + * + * This file can be freely distributed within development tools that are + * supporting such microcontrollers. + * + * + * @par + * THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED + * OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. + * INFINEON SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, + * OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER. + * + ******************************************************************************/ +/* + * *************************** Change history ******************************** + * V1.2, 13 Dec 2012, PKB : Created change history table + * V1.3, 20 Dec 2012, PKB : Fixed SystemCoreClock computation + * V1.4, 02 Feb 2013, PKB : SCU_CLOCK -> SCU_CLK + * V1.5, 27 Nov 2013, DNE : Comments added in SystemInit function for MCLK support + * V1.6, 19 Feb 2014, JFT : Fixed SystemCoreClock when FDIV != 0 + * V1.7, 11 Dec 2014, JFT : SystemCoreClockSetup, SystemCoreSetup as weak functions + */ + +/******************************************************************************* + * HEADER FILES + *******************************************************************************/ + +#include +#include "system_XMC1200.h" + +/******************************************************************************* + * MACROS + *******************************************************************************/ + +/* Define WEAK attribute */ +#if !defined(__WEAK) +#if defined ( __CC_ARM ) +#define __WEAK __attribute__ ((weak)) +#elif defined ( __ICCARM__ ) +#define __WEAK __weak +#elif defined ( __GNUC__ ) +#define __WEAK __attribute__ ((weak)) +#elif defined ( __TASKING__ ) +#define __WEAK __attribute__ ((weak)) +#endif +#endif + +#define DCO1_FREQUENCY (64000000U) + +/******************************************************************************* + * GLOBAL VARIABLES + *******************************************************************************/ + +#if defined ( __CC_ARM ) +uint32_t SystemCoreClock __attribute__((at(0x20003FFC))); +#elif defined ( __ICCARM__ ) +__no_init uint32_t SystemCoreClock; +#elif defined ( __GNUC__ ) +uint32_t SystemCoreClock __attribute__((section(".no_init"))); +#elif defined ( __TASKING__ ) +uint32_t SystemCoreClock __at( 0x20003FFC ); +#endif + +/******************************************************************************* + * API IMPLEMENTATION + *******************************************************************************/ + +__WEAK void SystemInit(void) +{ + SystemCoreSetup(); + SystemCoreClockSetup(); +} + +__WEAK void SystemCoreSetup(void) +{ +} + +__WEAK void SystemCoreClockSetup(void) +{ + /* Clock setup done during SSW using the CLOCK_VAL1 and CLOCK_VAL2 defined in vector table */ + SystemCoreClockUpdate(); +} + +__WEAK void SystemCoreClockUpdate(void) +{ + static uint32_t IDIV, FDIV; + + IDIV = ((SCU_CLK->CLKCR) & SCU_CLK_CLKCR_IDIV_Msk) >> SCU_CLK_CLKCR_IDIV_Pos; + FDIV = ((SCU_CLK->CLKCR) & SCU_CLK_CLKCR_FDIV_Msk) >> SCU_CLK_CLKCR_FDIV_Pos; + + if (IDIV != 0) + { + /* Fractional divider is enabled and used */ + SystemCoreClock = ((DCO1_FREQUENCY << 6U) / ((IDIV << 8) + FDIV)) << 1U; + } + else + { + /* Fractional divider bypassed. Simply divide DCO_DCLK by 2 */ + SystemCoreClock = DCO1_FREQUENCY >> 1U; + } +} -- libgit2 0.21.4