From e5c4924d6d38a91c8d2f6a1f6105e22088b10e03 Mon Sep 17 00:00:00 2001 From: Manfred Steiner Date: Sat, 5 Apr 2025 10:39:00 +0200 Subject: [PATCH] =?utf8?q?Software=20f=C3=BCr=20Wechselrichter=20Deye-SUN-?= =?utf8?q?12K?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- software/deye-sun-12k/.gdb_history | 9 + software/deye-sun-12k/.gdbinit | 2 + software/deye-sun-12k/.gitignore | 4 + .../.vscode/c_cpp_properties.json | 18 + software/deye-sun-12k/.vscode/launch.json | 37 + software/deye-sun-12k/.vscode/settings.json | 29 + software/deye-sun-12k/.vscode/tasks.json | 23 + software/deye-sun-12k/LICENSE | 21 + software/deye-sun-12k/Makefile | 22 + software/deye-sun-12k/README.md | 37 + software/deye-sun-12k/create-release | 120 + software/deye-sun-12k/create-release.old | 86 + software/deye-sun-12k/nano-1284/Makefile | 262 ++ ...x-base_test-software_nano-m1284p_12mhz.bin | Bin 0 -> 38616 bytes ...x-base_test-software_nano-m1284p_12mhz.elf | Bin 0 -> 76244 bytes ...x-base_test-software_nano-m1284p_12mhz.hex | 2416 +++++++++++++++++ software/deye-sun-12k/nano-1284/src | 1 + software/deye-sun-12k/nano-644/Makefile | 262 ++ ...-x-base_test-software_nano-m644p_12mhz.bin | Bin 0 -> 38296 bytes ...-x-base_test-software_nano-m644p_12mhz.elf | Bin 0 -> 74852 bytes ...-x-base_test-software_nano-m644p_12mhz.hex | 2396 ++++++++++++++++ software/deye-sun-12k/nano-644/src | 1 + software/deye-sun-12k/src/adafruit/bme280.cpp | 512 ++++ software/deye-sun-12k/src/adafruit/bme280.h | 373 +++ software/deye-sun-12k/src/adafruit/ens160.cpp | 374 +++ software/deye-sun-12k/src/adafruit/ens160.h | 188 ++ software/deye-sun-12k/src/adafruit/sensor.h | 224 ++ software/deye-sun-12k/src/i2cmaster.cpp | 155 ++ software/deye-sun-12k/src/i2cmaster.hpp | 30 + software/deye-sun-12k/src/i2cslave.cpp | 92 + software/deye-sun-12k/src/i2cslave.hpp | 32 + software/deye-sun-12k/src/main.cpp | 458 ++++ software/deye-sun-12k/src/main.hpp | 38 + software/deye-sun-12k/src/units/cc1101.cpp | 761 ++++++ software/deye-sun-12k/src/units/cc1101.hpp | 282 ++ software/deye-sun-12k/src/units/deye.cpp | 231 ++ software/deye-sun-12k/src/units/deye.hpp | 25 + software/deye-sun-12k/src/units/encoder.cpp | 138 + software/deye-sun-12k/src/units/encoder.hpp | 28 + software/deye-sun-12k/src/units/i2c.cpp | 216 ++ software/deye-sun-12k/src/units/i2c.hpp | 43 + software/deye-sun-12k/src/units/ieee485.cpp | 111 + software/deye-sun-12k/src/units/ieee485.hpp | 22 + software/deye-sun-12k/src/units/lcd.cpp | 443 +++ software/deye-sun-12k/src/units/lcd.hpp | 48 + software/deye-sun-12k/src/units/led.cpp | 139 + software/deye-sun-12k/src/units/led.hpp | 25 + software/deye-sun-12k/src/units/modbus.cpp | 160 ++ software/deye-sun-12k/src/units/modbus.hpp | 24 + software/deye-sun-12k/src/units/motor.cpp | 221 ++ software/deye-sun-12k/src/units/motor.hpp | 30 + software/deye-sun-12k/src/units/portexp.cpp | 195 ++ software/deye-sun-12k/src/units/portexp.hpp | 24 + software/deye-sun-12k/src/units/poti.cpp | 34 + software/deye-sun-12k/src/units/poti.hpp | 17 + software/deye-sun-12k/src/units/r2r.cpp | 48 + software/deye-sun-12k/src/units/r2r.hpp | 17 + software/deye-sun-12k/src/units/rgb.cpp | 152 ++ software/deye-sun-12k/src/units/rgb.hpp | 25 + software/deye-sun-12k/src/units/rtc8563.cpp | 253 ++ software/deye-sun-12k/src/units/rtc8563.hpp | 70 + software/deye-sun-12k/src/units/seg7.cpp | 283 ++ software/deye-sun-12k/src/units/seg7.hpp | 25 + software/deye-sun-12k/src/units/switch.cpp | 100 + software/deye-sun-12k/src/units/switch.hpp | 20 + software/deye-sun-12k/src/units/uart1.cpp | 65 + software/deye-sun-12k/src/units/uart1.hpp | 21 + 67 files changed, 12518 insertions(+) create mode 100644 software/deye-sun-12k/.gdb_history create mode 100644 software/deye-sun-12k/.gdbinit create mode 100644 software/deye-sun-12k/.gitignore create mode 100644 software/deye-sun-12k/.vscode/c_cpp_properties.json create mode 100644 software/deye-sun-12k/.vscode/launch.json create mode 100644 software/deye-sun-12k/.vscode/settings.json create mode 100644 software/deye-sun-12k/.vscode/tasks.json create mode 100644 software/deye-sun-12k/LICENSE create mode 100644 software/deye-sun-12k/Makefile create mode 100644 software/deye-sun-12k/README.md create mode 100755 software/deye-sun-12k/create-release create mode 100755 software/deye-sun-12k/create-release.old create mode 100644 software/deye-sun-12k/nano-1284/Makefile create mode 100755 software/deye-sun-12k/nano-1284/release/v2024-11-08_181806/nano-x-base_test-software_nano-m1284p_12mhz.bin create mode 100755 software/deye-sun-12k/nano-1284/release/v2024-11-08_181806/nano-x-base_test-software_nano-m1284p_12mhz.elf create mode 100644 software/deye-sun-12k/nano-1284/release/v2024-11-08_181806/nano-x-base_test-software_nano-m1284p_12mhz.hex create mode 120000 software/deye-sun-12k/nano-1284/src create mode 100644 software/deye-sun-12k/nano-644/Makefile create mode 100755 software/deye-sun-12k/nano-644/release/v2024-11-08_181803/nano-x-base_test-software_nano-m644p_12mhz.bin create mode 100755 software/deye-sun-12k/nano-644/release/v2024-11-08_181803/nano-x-base_test-software_nano-m644p_12mhz.elf create mode 100644 software/deye-sun-12k/nano-644/release/v2024-11-08_181803/nano-x-base_test-software_nano-m644p_12mhz.hex create mode 120000 software/deye-sun-12k/nano-644/src create mode 100644 software/deye-sun-12k/src/adafruit/bme280.cpp create mode 100644 software/deye-sun-12k/src/adafruit/bme280.h create mode 100644 software/deye-sun-12k/src/adafruit/ens160.cpp create mode 100644 software/deye-sun-12k/src/adafruit/ens160.h create mode 100644 software/deye-sun-12k/src/adafruit/sensor.h create mode 100644 software/deye-sun-12k/src/i2cmaster.cpp create mode 100644 software/deye-sun-12k/src/i2cmaster.hpp create mode 100644 software/deye-sun-12k/src/i2cslave.cpp create mode 100644 software/deye-sun-12k/src/i2cslave.hpp create mode 100644 software/deye-sun-12k/src/main.cpp create mode 100644 software/deye-sun-12k/src/main.hpp create mode 100644 software/deye-sun-12k/src/units/cc1101.cpp create mode 100644 software/deye-sun-12k/src/units/cc1101.hpp create mode 100644 software/deye-sun-12k/src/units/deye.cpp create mode 100644 software/deye-sun-12k/src/units/deye.hpp create mode 100644 software/deye-sun-12k/src/units/encoder.cpp create mode 100644 software/deye-sun-12k/src/units/encoder.hpp create mode 100644 software/deye-sun-12k/src/units/i2c.cpp create mode 100644 software/deye-sun-12k/src/units/i2c.hpp create mode 100644 software/deye-sun-12k/src/units/ieee485.cpp create mode 100644 software/deye-sun-12k/src/units/ieee485.hpp create mode 100644 software/deye-sun-12k/src/units/lcd.cpp create mode 100644 software/deye-sun-12k/src/units/lcd.hpp create mode 100644 software/deye-sun-12k/src/units/led.cpp create mode 100644 software/deye-sun-12k/src/units/led.hpp create mode 100644 software/deye-sun-12k/src/units/modbus.cpp create mode 100644 software/deye-sun-12k/src/units/modbus.hpp create mode 100644 software/deye-sun-12k/src/units/motor.cpp create mode 100644 software/deye-sun-12k/src/units/motor.hpp create mode 100644 software/deye-sun-12k/src/units/portexp.cpp create mode 100644 software/deye-sun-12k/src/units/portexp.hpp create mode 100644 software/deye-sun-12k/src/units/poti.cpp create mode 100644 software/deye-sun-12k/src/units/poti.hpp create mode 100644 software/deye-sun-12k/src/units/r2r.cpp create mode 100644 software/deye-sun-12k/src/units/r2r.hpp create mode 100644 software/deye-sun-12k/src/units/rgb.cpp create mode 100644 software/deye-sun-12k/src/units/rgb.hpp create mode 100644 software/deye-sun-12k/src/units/rtc8563.cpp create mode 100644 software/deye-sun-12k/src/units/rtc8563.hpp create mode 100644 software/deye-sun-12k/src/units/seg7.cpp create mode 100644 software/deye-sun-12k/src/units/seg7.hpp create mode 100644 software/deye-sun-12k/src/units/switch.cpp create mode 100644 software/deye-sun-12k/src/units/switch.hpp create mode 100644 software/deye-sun-12k/src/units/uart1.cpp create mode 100644 software/deye-sun-12k/src/units/uart1.hpp diff --git a/software/deye-sun-12k/.gdb_history b/software/deye-sun-12k/.gdb_history new file mode 100644 index 0000000..3339046 --- /dev/null +++ b/software/deye-sun-12k/.gdb_history @@ -0,0 +1,9 @@ +target remote :1234 +layout split +stepi +quit +target remote :1234 +layout split +stepi +b *main+9 +quit diff --git a/software/deye-sun-12k/.gdbinit b/software/deye-sun-12k/.gdbinit new file mode 100644 index 0000000..139597f --- /dev/null +++ b/software/deye-sun-12k/.gdbinit @@ -0,0 +1,2 @@ + + diff --git a/software/deye-sun-12k/.gitignore b/software/deye-sun-12k/.gitignore new file mode 100644 index 0000000..a959910 --- /dev/null +++ b/software/deye-sun-12k/.gitignore @@ -0,0 +1,4 @@ +.depend +**/build +**/dist +**/sim diff --git a/software/deye-sun-12k/.vscode/c_cpp_properties.json b/software/deye-sun-12k/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..87fd0ec --- /dev/null +++ b/software/deye-sun-12k/.vscode/c_cpp_properties.json @@ -0,0 +1,18 @@ +{ + "configurations": [ + { + "name": "Linux AVR", + "includePath": [ + "/usr/lib/avr/include/**", + "/usr/lib/gcc/avr/**" + ], + "defines": [], + "compilerPath": "/usr/bin/avr-g++", + "compilerArgs": [ "-mmcu=atmega1284p", "-DF_CPU=12000000", "-Os" ], + "cStandard": "gnu11", + "cppStandard": "gnu++11", + "intelliSenseMode": "linux-gcc-x64" + } + ], + "version": 4 +} diff --git a/software/deye-sun-12k/.vscode/launch.json b/software/deye-sun-12k/.vscode/launch.json new file mode 100644 index 0000000..f29cf2e --- /dev/null +++ b/software/deye-sun-12k/.vscode/launch.json @@ -0,0 +1,37 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Build", + // "request": "launch", + "type": "node-terminal", + "preLaunchTask": "build" + },{ + "name": "Flash", + // "request": "launch", + "type": "node-terminal", + "preLaunchTask": "flash" + },{ + "name": "Clean", + // "request": "launch", + "type": "node-terminal", + "preLaunchTask": "clean" + },{ + // es muss mit simuc --board arduino dist/programm.elf der Simulator + // gestartet werden. Dessen gdb-stub öffnet auf localhost:1234 einen Port + "name": "Debug (simuc)", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/sim/atmega328p.elf", + "cwd": "${workspaceFolder}", + "externalConsole": false, + "MIMode": "gdb", + "miDebuggerPath": "/usr/bin/avr-gdb", + "miDebuggerServerAddress": ":1234", + "preLaunchTask": "build" + } + ] +} diff --git a/software/deye-sun-12k/.vscode/settings.json b/software/deye-sun-12k/.vscode/settings.json new file mode 100644 index 0000000..58539af --- /dev/null +++ b/software/deye-sun-12k/.vscode/settings.json @@ -0,0 +1,29 @@ +{ + "[c]": { + "editor.insertSpaces": true, + "editor.tabSize": 3, + "editor.detectIndentation": false + }, + "[cpp]": { + "editor.insertSpaces": true, + "editor.tabSize": 3, + "editor.detectIndentation": false + }, + "[h]": { + "editor.insertSpaces": true, + "editor.tabSize": 3, + "editor.detectIndentation": false + }, + "[hpp]": { + "editor.insertSpaces": true, + "editor.tabSize": 3, + "editor.detectIndentation": false + }, + "cSpell.words": [], + "cSpell.ignorePaths": [ + "**/*.json", "**/*.c", "**/*.h", "**/*.cpp", "**/*.hpp", "**/Makefile" + ], + "java.project.sourcePaths": [ + "src/units" + ] +} diff --git a/software/deye-sun-12k/.vscode/tasks.json b/software/deye-sun-12k/.vscode/tasks.json new file mode 100644 index 0000000..74fb1c7 --- /dev/null +++ b/software/deye-sun-12k/.vscode/tasks.json @@ -0,0 +1,23 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [{ + "label": "build", + "type": "shell", + "command": "make", + "problemMatcher":[ + "$gcc" + ] + },{ + "label": "clean", + "type": "shell", + "command": "make", + "args": [ "clean" ], + },{ + "label": "flash", + "type": "shell", + "command": "make", + "args": [ "flash" ], + }] +} \ No newline at end of file diff --git a/software/deye-sun-12k/LICENSE b/software/deye-sun-12k/LICENSE new file mode 100644 index 0000000..c9565b9 --- /dev/null +++ b/software/deye-sun-12k/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Manfred Steiner (Manfred.Steiner@gmx.at) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/software/deye-sun-12k/Makefile b/software/deye-sun-12k/Makefile new file mode 100644 index 0000000..6c33dbc --- /dev/null +++ b/software/deye-sun-12k/Makefile @@ -0,0 +1,22 @@ +.PHONY: all release clean-all clean + +SEP = -------------------------------------------------------- + +all: + @printf "\n\e[1;36m$(SEP)\nNano-644\n$(SEP)\e[m\n" + -@make -C nano-644 + @printf "\n\e[1;36m$(SEP)\n Nano-1284\n$(SEP)\e[m\n" + -@make -C nano-1284 + +release: + -@make -C nano-644 release + -@make -C nano-1284 release + +clean-all: clean + test -d nano-644/release && rm -r nano-644/release + test -d nano-1284/release && rm -r nano-1284/release + +clean: + @make -C nano-644 clean + @make -C nano-1284 clean + diff --git a/software/deye-sun-12k/README.md b/software/deye-sun-12k/README.md new file mode 100644 index 0000000..cfc24f0 --- /dev/null +++ b/software/deye-sun-12k/README.md @@ -0,0 +1,37 @@ +# Test-Software for Nano-X-Base + +This software supports: +* Arduino Nano (ATmega328P, 16MHz, 5V) +* Nano-644 (ATmega644P, 12MHz, 3.3V) +* Nano-1284 (ATmega1284P, 12MHz, 3.3V) + +For the Nano-X-Base hardware version: +* V1a +* V2a + +## System requirements + +Makefiles and scripts are designed for running in a Linux system. +It is tested on a Debian system. + +## Build + +* To build all files use `make` +* To create release folder use `make release` +* To clean this folder use `make clean` + (release files are not removed) + +## Flashing the target + +Ensure that bootloader is available on target. Then change to the subdirectory of your target and execute `make flash`. + +Bootloader git repository: +[https://git.htl-mechatronik.at/public/?p=bootloader-arduino.git;a=home](https://git.htl-mechatronik.at/public/?p=bootloader-arduino.git;a=home) + +For example: +``` +user@pc:~/nano-x-base/software/test-software$ cd nano-328 +user@pc:~/nano-x-base/software/test-software/nano-328$ make flash +... +user@pc:~/nano-x-base/software/test-software/nano-328$ make picocom +``` diff --git a/software/deye-sun-12k/create-release b/software/deye-sun-12k/create-release new file mode 100755 index 0000000..90a8fff --- /dev/null +++ b/software/deye-sun-12k/create-release @@ -0,0 +1,120 @@ +#!/bin/sh +#set -x + +while [ ! -z "$1" ]; do + DIR="$1" + FILE="$2" + + #echo " creating release folder for file $FILE in directory $DIR ..." + + if [ ! -d "$DIR" ]; then + printf " [1;31mERROR: missing target directory $DIR\e[m\n" + exit 1 + fi + + if [ -z "$FILE" ] || [ ! -f "$FILE" ]; then + printf " [1;31mERROR: missing file $FILE\e[m\n" + exit 2 + fi + + shift + shift + + DATE_OBJDUMP=$(avr-objdump -tT "$FILE" 2>&1 | grep -e _.*DATE) + if [ -z "$DATE_OBJDUMP" ]; then + printf " \e[1;31mERROR ($FILE -> $DIR): symbol DATE not found\e[m\n" + exit 3 + fi + + TIME_OBJDUMP=$(avr-objdump -tT "$FILE" 2>&1 | grep -e _.*TIME) + if [ -z "$DATE_OBJDUMP" ]; then + printf " \e[1;31mERROR ($FILE -> $DIR): symbol TIME not found\e[m\n" + exit 4 + fi + + TEXT_SECT_READELF=$(readelf --syms ${FILE} | grep " .text") + if [ -z "$DATE_OBJDUMP" ]; then + printf " \e[1;31mERROR ($FILE -> $DIR): symbol .text not found\e[m\n" + exit 5 + fi + + avr-objcopy --dump-section .text=/tmp/.dumpsection.bin "$FILE" + SECT_TEXT_OFFSET_HEX="0x$(echo "$TEXT_SECT_READELF" | tr '\t' ' ' | tr -s ' ' | cut -d' ' -f 3)" + SECT_TEXT_OFFSET=$(( $(printf "%d" $SECT_TEXT_OFFSET_HEX) )) + + DATE_OFFSET_HEX="0x$(echo "$DATE_OBJDUMP" | cut -d " " -f 1)" + DATE_LENGTH_HEX="0x$(echo "$DATE_OBJDUMP" | tr '\t' ' ' | tr -s ' ' | cut -d' ' -f 5)" + DATE_OFFSET=$(( $(printf "%d" $DATE_OFFSET_HEX) - $SECT_TEXT_OFFSET )) + DATE_LENGTH=$(( $(printf "%d" $DATE_LENGTH_HEX) - 1)) + + TIME_OFFSET_HEX="0x$(echo "$TIME_OBJDUMP" | cut -d " " -f 1)" + TIME_LENGTH_HEX="0x$(echo "$TIME_OBJDUMP" | tr '\t' ' ' | tr -s ' ' | cut -d' ' -f 5)" + TIME_OFFSET=$(( $(printf "%d" $TIME_OFFSET_HEX) - $SECT_TEXT_OFFSET )) + TIME_LENGTH=$(( $(printf "%d" $TIME_LENGTH_HEX) - 1)) + + DATE_STRING=$(hexdump -s ${DATE_OFFSET} -n ${DATE_LENGTH} -e "${DATE_LENGTH} \"%_p\" \"\\n\"" /tmp/.dumpsection.bin) + TIME_STRING=$(hexdump -s ${TIME_OFFSET} -n ${TIME_LENGTH} -e "${TIME_LENGTH} \"%_p\" \"\\n\"" /tmp/.dumpsection.bin) + + DATE_MONTH_STRING=$(echo $DATE_STRING | cut -d' ' -f1) + case "$DATE_MONTH_STRING" in + Jan) DATE_MONTH="1";; + Feb) DATE_MONTH="2";; + Mar) DATE_MONTH="3";; + Apr) DATE_MONTH="4";; + May) DATE_MONTH="5";; + Jun) DATE_MONTH="6";; + Jul) DATE_MONTH="7";; + Aug) DATE_MONTH="8";; + Sep) DATE_MONTH="9";; + Oct) DATE_MONTH="10";; + Nov) DATE_MONTH="11";; + Dec) DATE_MONTH="12";; + *) printf " \e[1;31mERROR ($FILE -> $DIR): invalidate date ($DATE_STRINGS) in file $FILE\e[m\n"; exit 6;; + esac + + DATE_DAY=$(echo $DATE_STRING | cut -d' ' -f2 | sed 's/^0*//') + DATE_YEAR=$(echo $DATE_STRING | cut -d' ' -f3 | sed 's/^0*//') + TIME_VALUE=$(echo $TIME_STRING | cut -d' ' -f2-) + TIME_HOUR=$(echo $TIME_STRING | cut -d ':' -f1 | sed 's/^0*//') + TIME_MINUTE=$(echo $TIME_STRING | cut -d ':' -f2 | sed 's/^0*//') + TIME_SECOND=$(echo $TIME_STRING | cut -d ':' -f3 | sed 's/^0*//') + + NAME=$(printf "v%4d-%02d-%02d_%02d%02d%02d" $DATE_YEAR $DATE_MONTH $DATE_DAY $TIME_HOUR $TIME_MINUTE $TIME_SECOND | egrep ^v[0-9]{4}-[0-9]{2}-[0-9]{1,2}_[0-9]{2}[0-9]{2}[0-9]{2}$) + if [ -z "$NAME" ]; then + printf " \e[1;31mERROR ($FILE -> $DIR): cannot create release name\e[m\n" + exit 7 + fi + FILENAME=$(echo "$FILE" | rev | cut -d"/" -f1 | rev) + if [ -d "$DIR/$NAME" ] && [ -f "$DIR/$NAME/$FILENAME" ]; then + echo " OK: release already created ($FILE -> $DIR/$NAME)" + else + test -d "$DIR/$NAME" || mkdir "$DIR/$NAME" + cp -a "$FILE" "$DIR/$NAME/" + if [ $? = 0 ]; then + echo " OK ($FILE -> $DIR/$NAME)" + else + printf " \e[1;31mERROR ($FILE -> $DIR)\e[m\n" + exit 8 + fi + fi + for suffix in "hex" "bin"; do + SUFFIXFILE=$(echo $FILE | sed "s/.elf$/.${suffix}/") + SUFFIXFILENAME=$(echo "$SUFFIXFILE" | rev | cut -d"/" -f1 | rev) + if [ -f "$SUFFIXFILE" ]; then + if [ -d "$DIR/$NAME" ] && [ -f "$DIR/$NAME/$SUFFIXFILENAME" ]; then + echo " OK: release ${suffix}-file already created ($FILE -> $DIR/$NAME)" + else + test -d "$DIR/$NAME" || mkdir "$DIR/$NAME" + cp -a "$SUFFIXFILE" "$DIR/$NAME/" + if [ $? = 0 ]; then + echo " OK ($SUFFIXFILE -> $DIR/$NAME)" + else + printf " \e[1;31mERROR ($SUFFIXFILE -> $DIR)\e[m\n" + exit 8 + fi + fi + fi + done + +done + diff --git a/software/deye-sun-12k/create-release.old b/software/deye-sun-12k/create-release.old new file mode 100755 index 0000000..225277f --- /dev/null +++ b/software/deye-sun-12k/create-release.old @@ -0,0 +1,86 @@ +#!/bin/sh +#set -x + +while [ ! -z "$1" ]; do + DIR="$1" + FILE="$2" + + #echo " creating release folder for file $FILE in directory $DIR ..." + + if [ ! -d "$DIR" ]; then + printf " [1;31mERROR: missing target directory $DIR\e[m\n" + exit 1 + fi + + if [ -z "$FILE" ] || [ ! -f "$FILE" ]; then + printf " [1;31mERROR: missing file $FILE\e[m\n" + exit 2 + fi + + shift + shift + + DATE_OBJDUMP=$(avr-objdump -tT "$FILE" 2>&1 | grep MAIN_CPP_DATE) + if [ -z "$DATE_OBJDUMP" ]; then + printf " [1;31mERROR ($FILE -> %DIR): symbol MAIN_CPP_DATE not found\e[m\n" + exit 3 + fi + + TIME_OBJDUMP=$(avr-objdump -tT "$FILE" 2>&1 | grep MAIN_CPP_TIME) + if [ -z "$DATE_OBJDUMP" ]; then + printf " [1;31mERROR ($FILE -> %DIR): symbol MAIN_CPP_TIME not found\e[m\n" + exit 4 + fi + + DATE_OFFSET_HEX="0x$(echo "$DATE_OBJDUMP" | cut -d " " -f 1)" + TIME_OFFSET_HEX="0x$(echo "$TIME_OBJDUMP" | cut -d " " -f 1)" + DATE_OFFSET=$(( $(printf "%d" $DATE_OFFSET_HEX) + 148 )) + TIME_OFFSET=$(( $(printf "%d" $TIME_OFFSET_HEX) + 148 )) + + DATE_STRINGS=$(strings -a -t d "$FILE" | grep "$(printf "%7s" "$DATE_OFFSET")") + TIME_STRINGS=$(strings -a -t d "$FILE" | grep "$(printf "%7s" "$TIME_OFFSET")") + + DATE_MONTH_STRING=$(echo $DATE_STRINGS | cut -d' ' -f2) + case "$DATE_MONTH_STRING" in + Jan) DATE_MONTH="01";; + Feb) DATE_MONTH="02";; + Mar) DATE_MONTH="03";; + Apr) DATE_MONTH="04";; + May) DATE_MONTH="05";; + Jun) DATE_MONTH="06";; + Jul) DATE_MONTH="07";; + Aug) DATE_MONTH="08";; + Sep) DATE_MONTH="09";; + Oct) DATE_MONTH="10";; + Nov) DATE_MONTH="11";; + Dec) DATE_MONTH="12";; + *) printf " [1;31mERROR ($FILE -> %DIR): invalidate date in file $FILE\e[m\n"; exit 5;; + esac + + DATE_DAY=$(echo $DATE_STRINGS | cut -d' ' -f3) + DATE_YEAR=$(echo $DATE_STRINGS | cut -d' ' -f4) + TIME_VALUE=$(echo $TIME_STRINGS | cut -d' ' -f2-) + TIME_HOUR=$(echo $TIME_VALUE | cut -d ':' -f1) + TIME_MINUTE=$(echo $TIME_VALUE | cut -d ':' -f2) + TIME_SECOND=$(echo $TIME_VALUE | cut -d ':' -f3) + + NAME=$(printf "v%4d-%02d-%02d_%02d%02d%02d" $DATE_YEAR $DATE_MONTH $DATE_DAY $TIME_HOUR $TIME_MINUTE $TIME_SECOND | egrep ^v[0-9]{4}-[0-9]{2}-[0-9]{1,2}_[0-9]{2}[0-9]{2}[0-9]{2}$) + if [ -z "$NAME" ]; then + printf " [1;31mERROR ($FILE -> %DIR): cannot create release name\e[m\n" + exit 6 + fi + FILENAME=$(echo "$FILE" | rev | cut -d"/" -f1 | rev) + if [ -d "$DIR/$NAME" ] && [ -f "$DIR/$NAME/$FILENAME" ]; then + echo " OK: release already done ($FILE -> $DIR/$NAME)" + else + test -d "$DIR/$NAME" || mkdir "$DIR/$NAME" + cp -a "$FILE" "$DIR/$NAME/" + if [ $? = 0 ]; then + echo " OK ($FILE -> $DIR/$NAME)" + else + printf " [1;31mERROR ($FILE -> %DIR)\e[m\n" + fi + fi + +done + diff --git a/software/deye-sun-12k/nano-1284/Makefile b/software/deye-sun-12k/nano-1284/Makefile new file mode 100644 index 0000000..0c210b6 --- /dev/null +++ b/software/deye-sun-12k/nano-1284/Makefile @@ -0,0 +1,262 @@ +$(shell mkdir -p dist >/dev/null) +$(shell mkdir -p build >/dev/null) +$(shell mkdir -p sim >/dev/null) +$(shell mkdir -p sim/build >/dev/null) +$(shell mkdir -p release/sim >/dev/null) + +# -------------------------------------------------------------------------------- +# Variables configured by engineer + +NAME=nano-x-base_deye-sun-12k_nano-m1284p_12mhz +DEVICE=atmega1284p +AVRDUDE_DEVICE=m1284p +CPU_FREQUENCY=12000000 +BAUDRATE=115200 +START_ADDRESS=0 + +# -------------------------------------------------------------------------------- +# Automatic created Makefile variables + +SRC= $(wildcard src/*.c src/*.cpp src/*/*.c src/*/*.cpp) +HDR= $(wildcard src/*.h src/*.hpp src/*/*.h src/*/*.hpp) +MAINSRC= $(wildcard src/main.c src/main.cpp) +OBJ_CPP = $(SRC:src/%.cpp=build/%.o) +OBJ = $(OBJ_CPP:src/%.c=build/%.o) +OBJ_SIM_CPP = $(SRC:src/%.cpp=sim/build/%.o) +OBJ_SIM = $(OBJ_SIM_CPP:src/%.c=sim/build/%.o) + +CC= avr-g++ +CFLAGS= -Wall -mmcu=$(DEVICE) -Os -DF_CPU=$(CPU_FREQUENCY) -DBAUD_RATE=$(BAUDRATE) -DDOUBLE_SPEED -DNUM_LED_FLASHES=4 '-DMAX_TIME_COUNT=F_CPU>>4' -c +LFLAGS= -Wall -mmcu=$(DEVICE) -Os -DF_CPU=$(CPU_FREQUENCY) -Wl,--section-start=.text=$(START_ADDRESS) -Wl,-u,vfprintf -lprintf_flt -lm +#LFLAGS= -Wall -mmcu=$(DEVICE) -Os -DF_CPU=$(CPU_FREQUENCY) -Wl,--section-start=.text=$(START_ADDRESS) + +CFLAGS_SIM= -Wall -mmcu=$(DEVICE) -Og -DF_CPU=$(CPU_FREQUENCY) -g -DBAUD_RATE=$(BAUDRATE) -DDOUBLE_SPEED -DNUM_LED_FLASHES=4 '-DMAX_TIME_COUNT=F_CPU>>4' -c +LFLAGS_SIM= -Wall -mmcu=$(DEVICE) -Og -DF_CPU=$(CPU_FREQUENCY) -g -Wl,--section-start=.text=$(START_ADDRESS) -Wl,-u,vfprintf -lprintf_flt -lm +#LFLAGS_SIM= -Wall -mmcu=$(DEVICE) -Og -DF_CPU=$(CPU_FREQUENCY) -g -Wl,--section-start=.text=$(START_ADDRESS) + +# -------------------------------------------------------------------------------- +# make targets + +.PHONY: all +all: dist/$(NAME).elf sim/$(NAME).elf dist/$(NAME).s dist/$(NAME).hex dist/$(NAME).bin sim/$(NAME).s info + +.PHONY: info +info: + @echo + @avr-size --mcu=$(DEVICE) --format=avr dist/$(NAME).elf + +# -------------------------------------------------------------------------------- +# dependency make for hierarchical source file structure + +.depend: $(SRC) $(HDR) + $(CC) -mmcu=$(DEVICE) -MM $(SRC) | sed --regexp-extended 's/^(.*\.o)\: src\/(.*)(\.cpp|\.c) (.*)/build\/\2\.o\: src\/\2\3 \4/g' > .depend + +ifneq (clean,$(filter clean,$(MAKECMDGOALS))) +-include .depend +endif + +# -------------------------------------------------------------------------------- +# elf, hex and assembler file creation + +dist/$(NAME).elf: .depend $(OBJ) + $(CC) $(LFLAGS) -o $@ $(OBJ) + +dist/%.s: dist/%.elf + avr-objdump -d $< > $@ + +dist/%.hex: dist/%.elf + avr-objcopy -O ihex $(HEX_FLASH_FLAGS) $< $@ + +dist/%.bin: dist/%.elf + avr-objcopy -O binary $(HEX_FLASH_FLAGS) $< $@ + +# -------------------------------------------------------------------------------- +# check if the macros __DATE__ or __TIME__ are used in src/main.cpp or src/main.c + +DATE_USED= +ifneq ($(shell cat $(MAINSRC) | grep __DATE__),) + DATE_USED=true +endif +TIME_USED= +ifneq ($(shell cat $(MAINSRC) | grep __TIME__),) + TIME_USED=true +endif + +ifeq (true, $(filter true, $(DATE_USED) $(TIME_USED))) +build/main.o: $(MAIN_SRC) $(SRC) $(HDR) + @mkdir -p $(dir $@) + $(CC) $(CFLAGS) -o $@ $< +endif + +# -------------------------------------------------------------------------------- + +build/%.o: src/%.c + @mkdir -p $(dir $@) + $(CC) $(CFLAGS) -o $@ $< + +build/%.o: src/%.cpp + @mkdir -p $(dir $@) + $(CC) $(CFLAGS) -o $@ $< + +# -------------------------------------------------------------------------------- +# simulation/debugging with gdb or simuc + +sim/$(NAME).elf: .depend $(OBJ_SIM) + $(CC) $(LFLAGS_SIM) -o $@ $(OBJ_SIM) + @ln -sf $(NAME).elf sim/$(DEVICE).elf + +sim/build/%.o: src/%.c + @mkdir -p $(dir $@) + $(CC) $(CFLAGS_SIM) -o $@ $< + +sim/build/%.o: src/%.cpp + @mkdir -p $(dir $@) + $(CC) $(CFLAGS_SIM) -o $@ $< + +sim/%.s: sim/%.elf + avr-objdump -d $< > $@ + +ifeq (m16, $(AVRDUDE_DEVICE)) +simuc: sim/$(NAME).elf + simuc --board sure $< +endif + +ifeq (m328p, $(AVRDUDE_DEVICE)) +simuc: sim/$(NAME).elf + simuc --board arduino $< +endif + +ifeq (m644p, $(AVRDUDE_DEVICE)) +simuc: sim/$(NAME).elf + simuc --board nano-644 $< +endif + +ifeq (m1284p, $(AVRDUDE_DEVICE)) +simuc: sim/$(NAME).elf + simuc --board nano-1284 $< +endif + +gdb: sim/$(NAME).elf + avr-gdb $< + +# ------------------------------------------------------------- +# flash to target with arduino bootloader in bootloader-section + +.PHONY: flash +flash: dist/$(NAME).elf all + avrdude -c arduino -P /dev/ttyUSB0 -b $(BAUDRATE) -p $(AVRDUDE_DEVICE) -e -U flash:w:$< + +.PHONY: flash0 +flash0: dist/$(NAME).elf all + avrdude -c arduino -P /dev/ttyUSB0 -b $(BAUDRATE) -p $(AVRDUDE_DEVICE) -e -U flash:w:$< + +.PHONY: flash1 +flash1: dist/$(NAME).elf all + avrdude -c arduino -P /dev/ttyUSB1 -b $(BAUDRATE) -p $(AVRDUDE_DEVICE) -e -U flash:w:$< + +.PHONY: flash2 +flash2: dist/$(NAME).elf all + avrdude -c arduino -P /dev/ttyUSB2 -b $(BAUDRATE) -p $(AVRDUDE_DEVICE) -e -U flash:w:$< + +.PHONY: flash-read +flash-read: + avrdude -c arduino -P /dev/ttyUSB0 -b $(BAUDRATE) -p $(AVRDUDE_DEVICE) -U flash:r:/tmp/flash.bin + +.PHONY: flash-disassemble +flash-disassemble: flash-read + avr-objdump -b binary -D -m avr5 /tmp/flash.bin > /tmp/flash.s + less /tmp/flash.s + +.PHONY: flash-hexdump +flash-hexdump: flash-read + hexdump -C /tmp/flash.bin | less + +# ---------------------------------------------- +# flash to target with fischl programming device + +.PHONY: isp-flash-$(AVRDUDE_DEVICE) +isp-$(AVRDUDE_DEVICE): + avrdude -c usbasp -p $(AVRDUDE_DEVICE) -U lock:r:-:h + avrdude -c usbasp -p $(AVRDUDE_DEVICE) + +.PHONY: isp-flash +isp-flash: dist/$(NAME).elf all + avrdude -c usbasp -p $(AVRDUDE_DEVICE) -e -U flash:w:$< + +.PHONY: isp-flash-$(AVRDUDE_DEVICE) +isp-flash-$(AVRDUDE_DEVICE): dist/$(NAME).elf all + avrdude -c usbasp -p $(AVRDUDE_DEVICE) -e -U flash:w:$< + +.PHONY: isp-read-flash-$(AVRDUDE_DEVICE) +isp-read-flash-$(AVRDUDE_DEVICE): + avrdude -c usbasp -p m32$(AVRDUDE_DEVICE)8p -U flash:r:/tmp/flash-arduino-atmega328p__$(shell date +"%Y-%m-%d_%H%M%S") + +.PHONY: isp-fuse +isp-fuse: isp-fuse-$(AVRDUDE_DEVICE) + +.PHONY: isp-fuse-$(AVRDUDE_DEVICE) +ifeq (m16, $(AVRDUDE_DEVICE)) +isp-fuse-$(AVRDUDE_DEVICE): + avrdude -c usbasp -p $(AVRDUDE_DEVICE) -U lfuse:w:0x18:m -U hfuse:w:0xD8:m -U lock:w:0xEF:m +endif +ifeq (m328p, $(AVRDUDE_DEVICE)) +isp-fuse-$(AVRDUDE_DEVICE): + avrdude -c usbasp -p $(AVRDUDE_DEVICE) -U lfuse:w:0xEF:m -U hfuse:w:0xD8:m -U efuse:w:0xFD:m -U lock:w:0xEF:m +endif +ifeq (m644p, $(AVRDUDE_DEVICE)) +isp-fuse-$(AVRDUDE_DEVICE): + avrdude -c usbasp -p $(AVRDUDE_DEVICE) -U lfuse:w:0xEF:m -U hfuse:w:0xD8:m -U efuse:w:0xFD:m -U lock:w:0xEF:m +endif +ifeq (m1284p, $(AVRDUDE_DEVICE)) +isp-fuse-$(AVRDUDE_DEVICE): + avrdude -c usbasp -p $(AVRDUDE_DEVICE) -U lfuse:w:0xEF:m -U hfuse:w:0xD8:m -U efuse:w:0xFD:m -U lock:w:0xEF:m +endif + +# -------------------------------------------------------- +# picocom sends CR for ENTER -> convert cr (\r) to lf (\n) + +.PHONY: picocom +picocom: + picocom -b $(BAUDRATE) --omap crlf --raise-dtr /dev/ttyUSB0 + +.PHONY: picocom0 +picocom0: + picocom -b $(BAUDRATE) --omap crlf --raise-dtr /dev/ttyUSB0 + +.PHONY: picocom1 +picocom1: + picocom -b $(BAUDRATE) --omap crlf --raise-dtr /dev/ttyUSB1 + +.PHONY: picocom2 +picocom2: + picocom -b $(BAUDRATE) --omap crlf --raise-dtr /dev/ttyUSB2 + +# -------------------------------------------------------- + +.PHONY: help +help: + @echo + @echo "Possible targets are:" + @echo " clean" + @echo " all help info" + @echo " flash flash0 flash1 flash2 flash-read flash-disassemble flash-hexdump" + @echo " isp-$(AVRDUDE_DEVICE) isp-flash-$(AVRDUDE_DEVICE) isp-fuse-$(AVRDUDE_DEVICE)" + @echo " picocom picocom0 picocom1 picocom2" + @echo " gdb simuc" + @echo + +# -------------------------------------------------------- + +.PHONY: release +release: dist/$(NAME).elf sim/$(NAME).elf dist/$(NAME).hex dist/$(NAME).bin + ../create-release release $(word 1, $^) release/sim $(word 2, $^) + +# -------------------------------------------------------- + +.PHONY: clean +clean: + @rm -r dist + @rm -r build + @rm -r sim + @find . -type f -name ".depend" -exec rm {} \; + @echo "clean done" diff --git a/software/deye-sun-12k/nano-1284/release/v2024-11-08_181806/nano-x-base_test-software_nano-m1284p_12mhz.bin b/software/deye-sun-12k/nano-1284/release/v2024-11-08_181806/nano-x-base_test-software_nano-m1284p_12mhz.bin new file mode 100755 index 0000000000000000000000000000000000000000..06e910a39b8ad1e71c03f5a9374fe59d38c9c043 GIT binary patch literal 38616 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 GK>ZJ+e5CIH literal 0 HcmV?d00001 diff --git a/software/deye-sun-12k/nano-1284/release/v2024-11-08_181806/nano-x-base_test-software_nano-m1284p_12mhz.elf b/software/deye-sun-12k/nano-1284/release/v2024-11-08_181806/nano-x-base_test-software_nano-m1284p_12mhz.elf new file mode 100755 index 0000000000000000000000000000000000000000..31affaa4844d2d22df12db90cc525353abe44eea GIT binary patch literal 76244 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 literal 0 HcmV?d00001 diff --git a/software/deye-sun-12k/nano-1284/release/v2024-11-08_181806/nano-x-base_test-software_nano-m1284p_12mhz.hex b/software/deye-sun-12k/nano-1284/release/v2024-11-08_181806/nano-x-base_test-software_nano-m1284p_12mhz.hex new file mode 100644 index 0000000..11d524a --- /dev/null +++ b/software/deye-sun-12k/nano-1284/release/v2024-11-08_181806/nano-x-base_test-software_nano-m1284p_12mhz.hex @@ -0,0 +1,2416 @@ +:100000000C94BA070C94E4070C94E4070C94E407EE +:100010000C94E4070C94E4070C94E4070C94E407B4 +:100020000C94E4070C94E00B0C94E4070C94E407A4 +:100030000C94E4070C94E4070C94E4070C94E40794 +:100040000C94E4070C94E4070C94E4070C94E40784 +:100050000C94240B0C94E4070C94E4070C94E40730 +:100060000C94E4070C94E4070C94A00B0C94E407A4 +:100070000C945B0B0C94E4070C94E4070C94E407D9 +:100080000C94E4070C94E4070C94E40707634236ED +:10009000B79BD8A71A39685618AEBAAB558C1D3C19 +:1000A000B7CC5763BD6DEDFD753EF6177231BF00DD +:1000B0000000803F08000000BE922449123EABAA17 +:1000C000AA2ABECDCCCC4C3E00000080BEABAAAA72 +:1000D000AA3E00000000BF000000803F00000000BA +:1000E00000084178D3BB4387D1133D190E3CC3BDF3 +:1000F0004282AD2B3E68EC8276BED98FE1A93E4CA0 +:1001000080EFFFBE01C4FF7F3F00000000006E6172 +:100110006E00696E660000407A10F35A00A0724EBD +:1001200018090010A5D4E80000E87648170000E49C +:100130000B54020000CA9A3B000000E1F5050000E4 +:1001400080969800000040420F000000A086010049 +:100150000000102700000000E80300000000640019 +:10016000000000000A000000000001000000000084 +:100170002C76D888DC674F0823DFC1DFAE59E1B1A8 +:10018000B796E5E3E453C63AE651997696E8E6C2B7 +:100190008426EB898C9B62ED407C6FFCEFBC9C9FBE +:1001A00040F2BAA56FA5F490055A2AF75C936B6CE0 +:1001B000F9676DC11BFCE0E40D47FEF520E6B500D4 +:1001C000D0ED902E0300943577050080841E080042 +:1001D00000204E0A000000C80C333333330F986EF2 +:1001E00012831141EF8D2114893BE65516CFFEE6AF +:1001F000DB18D1844B381BF77C1D901DA4BBE42475 +:10020000203284725E228100C9F124ECA1E53D27F1 +:100210006364696E6F70737578585B000A25346487 +:100220003A20005D3A20000A0A5B000A53656C65BB +:10023000637420756E69743A2000253378202E2E61 +:100240002E2000417661696C61626C6520756E6973 +:1002500074733A0A0A004E6F204E616E6F2D582D4E +:1002600042617365206465746563746564004E6102 +:100270006E6F2D582D426173653A20255300202F53 +:100280002000202F20000A0A48617264776172659D +:100290002025532064657465637465642028414497 +:1002A0004337483D30782530325829000A496E7668 +:1002B000616C6964204E616E6F2D582D42617365CB +:1002C0002048617264776172652D56657273696F3B +:1002D0006E3A204144433748203D20256420284180 +:1002E000546D65676131323834502C20332E3356CB +:1002F000290A00563F3F00563261005631610044E2 +:100300006F6E65004552524F52000A000A3D3D3D56 +:100310003D3D3D3D3D3D3D3D3D3D3D3D3D3D3D3D0D +:100320003D3D3D3D3D3D3D3D3D3D3D3D3D3D3D3DFD +:100330003D3D0A200041546D6567613132383450CB +:100340000031383A31383A3036004E6F7620203856 +:100350002032303234000A505441424C452025644A +:100360003A203078253032780020213D2030782521 +:10037000303278000A00205B5741524E3A206275B5 +:100380006666657220746F20736D616C6C5D200011 +:10039000204F4B5B7265636569766520256420623A +:1003A000797465735D2000204552524F525B72652F +:1003B00063656976652025642062797465732C20F5 +:1003C0006578706563742025642062797465735D57 +:1003D00020005D20002030782530325800205374F2 +:1003E000617465735B004552525D005D002030789A +:1003F00025303278000A205B726561645265676956 +:100400007374657273283078253032782C202E2E44 +:100410002E2C202564295D202D3E20004552525D62 +:10042000003078253032782C7374617475733D30E8 +:1004300078253032785D000A205B72656164526510 +:100440006769737465722830782530327829202DD9 +:100450003E20004552525D007374617475733D30E7 +:1004600078253032785D00202D3E20002030782520 +:10047000303278000A205B7772697465526567696B +:10048000737465727328307825303278293A2000E9 +:100490004552525D007374617475733D3078253038 +:1004A00032785D000A205B7772697465526567690E +:1004B00073746572283078253032782C2030782596 +:1004C00030327829202D3E20004F4B006661696C48 +:1004D00073004F4B2C20696E697420504154414287 +:1004E0004C45202E2E2E20006661696C73004F4B08 +:1004F0002C20696E69742072656769737465722057 +:100500002E2E2E20006661696C7300207265736563 +:100510007420434331313031202E2E2E20004343AE +:100520002D313130312D3F0043432D313130312DCC +:10053000546573740043432D313130312D5265635E +:10054000656976650043432D313130312D53656E39 +:100550006400646F6E65202873746174653D307843 +:10056000253032782900202E2E2E200020253032F2 +:100570005800256420646174612062797465732079 +:1005800028484558293A20004552524F522C200005 +:100590004F4B2C200020525353493D25642C204CB6 +:1005A00051493D25642C204352432000202D3E20FC +:1005B0000045320063616E63656C6C6564004532B2 +:1005C000004F4B2C2072656365697665202E2E2EB8 +:1005D00020003F202849444C45290063616E636533 +:1005E0006C6C6564004531000A205B253034785D11 +:1005F000203D3E207374617274202E2E2E20004FF9 +:100600004B004531005D202D3E2000202530325822 +:1006100000202D2D3E2073656E64202564206279B4 +:1006200074657320284845583A20253032580030E8 +:100630007825303278004531000A205B2530347847 +:100640005D3A2073746174653D006661696C730086 +:100650002850415441424C45203D203078253032CD +:1006600058290064426D20000A207377697463681A +:1006700020706F77657220746F200020757365207D +:100680002B20616E64202D20666F7220706F77655D +:1006900072206368616E676520286F7468657220D8 +:1006A0006B6579202D3E206261636B20746F20643E +:1006B000656661756C742900292E06477A0E14044C +:1006C0000400000600216276CAF81622F8400730BE +:1006D00018166C434991876BFB5610E92A001F419D +:1006E00000597F3F813509C6392E3600C0313100AF +:1006F00000C531300000CD37000000863500000015 +:100700005030000000372D360000262D3130001DFE +:100710002D313500172D323000032D3330002535B3 +:100720006420283078253032782920000D20203DA3 +:100730003E20456E636F6465722028707573682073 +:10074000746F20636C656172293A2000456E636F97 +:1007500064657200656E64000A2049324320534C80 +:100760004156453A2066726F6D206D6173746572F3 +:100770003A20307825303278202D3E20746F206D5D +:1007800061737465723A20307825303278004531D3 +:1007900000202D3E204552524F52003078253032F5 +:1007A00078002C202066726F6D20736C6176653A3C +:1007B0002000202D3E204552524F52000A2049323F +:1007C000432D4D41535445523A20746F20736C6150 +:1007D00076653A203078253032780045310045334F +:1007E00000290062616400706F6F72006661697257 +:1007F00000676F6F6400657863656C6C656E74008C +:100800003F002C2065636F323D25642800292C2091 +:1008100074766F633D2564707062003F00756E688A +:1008200065616C74687900706F6F72006D6F6465DC +:100830007261746500676F6F6400657863656C6CE6 +:10084000656E7400206171693D25642800453200A1 +:10085000252E3166C2B0432F252E31662525293A33 +:1008600020004531290020207C20454E5331363070 +:100870002028002C20483D20252E32662525002CDE +:1008800020543D20252E3266C2B043000A203D3E52 +:1008900020424D3238303A20503D20252E336662BA +:1008A0006172004F4B004534004533004532004F24 +:1008B0004B2C20454E53313630202E2E2E20004515 +:1008C000310020424D323830202E2E2E2000493269 +:1008D000432D536C617665004932432D4D6173742D +:1008E0006572004932432D537061726B66756E20DC +:1008F000456E762D436F6D626F00656E64000A2051 +:10090000203D3E2072656365697665204279746595 +:100910003A20307825303278000A20203D3E20737E +:10092000656E642042797465203078253032780015 +:100930004945454534383500656E64004552524F8F +:100940005228256429004F4B004C43442000455257 +:10095000524F5220256429004F4B2900696E69745B +:10096000204C43442028004C636400546573742079 +:100970004C454420442564004C656400656E640069 +:10098000202D3E2025342E3866560A00203078254A +:10099000303278002020003F000A20202020202034 +:1009A00020527844313A0020307825303278000ADD +:1009B000203D3E2053656E64696E673A004D6F645A +:1009C0006275733A206C657365205370616E6E7545 +:1009D0006E6720766F6E2045617374726F6E205360 +:1009E000444D2D323330202845696E70686173653F +:1009F0006E7AC3A4686C65722900202E2E2E20709A +:100A000072657373206B657920746F2070726F63E9 +:100A1000656564006E6F2062797465207265636538 +:100A200069766564003078253032782072656365B8 +:100A300069766564002044453D25752C206E52453D +:100A40003D25752073656E64203078253032782E10 +:100A50002E2E2000696E6974004D6F6462757300FC +:100A600020206E6F20726F746174696F6E2028543D +:100A70003D253034782920200020206E3D2025356A +:100A80006420552F6D696E2028543D253034782917 +:100A9000002053454E534F523D25642000206E46A2 +:100AA00041554C543D25640020414443323D25339B +:100AB0006400202050574D2F4F4330413D25336473 +:100AC000000D20203D3E20414443303D253364004D +:100AD000454E3D3100454E3D30000D205357333DCE +:100AE00025642D3E000A004D6F746F72000A2020AD +:100AF00042616E6B30202D204750422564203D20FE +:100B000030000A202042616E6B30202D2047504279 +:100B10002564203D2031000A202042616E6B302088 +:100B20002D204750412564203D2030000A202042DE +:100B3000616E6B30202D204750412564203D2031CF +:100B4000000A203D3E207374617274202E2E2E0008 +:100B50002900204F4B00204A5033392E322F3320AA +:100B60006A756D706572656420286C656674293FCE +:100B700000204552524F5200202872656164203097 +:100B80007825303278202D3E20307825303278009C +:100B9000506F7274457870002534642028307825B1 +:100BA00030337829000D20203D3E204D656173755E +:100BB000726520414443303A20000A00506F746946 +:100BC000002025332E3166203D3E205357393A36DA +:100BD000203D2025642025642520642025642020D4 +:100BE00000253464202830782530337829000D2002 +:100BF000203D3E204D65617375726520414443324E +:100C00003A20000A0052325200416C6C00426C756E +:100C10006500477265656E00526564005267620048 +:100C20002025303278000A2025702025702025707C +:100C3000202570202570202570202570202D3E2035 +:100C400077726974653A2000706F776572206F66FD +:100C500066206661696C732C204920616D20737475 +:100C6000696C6C20616C697665203A2D29202E2EE6 +:100C70002E2070726F63656564000A20706F77655F +:100C800072206F6666206E6F77202E2E2E000D204C +:100C900070726573732045534320746F2061626FD7 +:100CA00072742C20706F776572206F666620696E93 +:100CB0002025647320287072657373206B6579201A +:100CC000746F20736B69702074696D6572292000E0 +:100CD0000A000A20706F776572206F6E202E2E2E0C +:100CE000006661696C73004F4B000A2054696D65A2 +:100CF00072206F6666202E2E2E20000A2054696D09 +:100D000065722073657420746F2025647320283108 +:100D10003A2530325829202E2E2E20000A207365C5 +:100D2000743A20253034642D253032642D2530323C +:100D30006420253032643A253032643A25303264FA +:100D400000202D2054696D65723D30782530327851 +:100D5000002C20256420253034642D253032642D6C +:100D60002530326420253032643A253032643A2509 +:100D70003032640020202D2D3E20003F3F002025F2 +:100D80003032580020000A203D3E20726561642008 +:100D9000726567697374657220302D31352028685B +:100DA0006578293A000A202020732F53202E2E2008 +:100DB0007365636F6E6420282B2F2D290A000A208B +:100DC00020206E2F4E202E2E206D696E75746520AA +:100DD000282B2F2D29000A202020682F48202E2E76 +:100DE00020686F757220282B2F2D29000A202020C3 +:100DF000642F44202E2E2064617920282B2F2D294A +:100E0000000A2020206D2F4D202E2E206D6F6E7435 +:100E10006820282B2F2D29000A202020792F5920E7 +:100E20002E2E207965617220282B2F2D29000A2073 +:100E30002020772F57202E2E207765656B646179EF +:100E400020282B2F2D290A000A20202063202E2E57 +:100E50002E2E20696E697420636C6F636B000A200C +:100E6000202070202E2E2E2E20706F776572206F1E +:100E70006E2F6F666620285043372D3E5131290072 +:100E80000A20202074202E2E2E2E2074696D65726B +:100E9000206F6E2F6F6666000A2070726573733A5A +:100EA000000A203D3E20636F6E6669672038353644 +:100EB00033202E2E2E20005254432D383536330049 +:100EC000536F004D6F004469004D6900446F004648 +:100ED00072005361000043686172202564202D2058 +:100EE0002573202D3E2025303278004C312F4C3296 +:100EF000204F4E004C312F4C32204F4E004F464673 +:100F0000004F4E00536567370052656C656173652D +:100F10002053572564200050726573732053572562 +:100F200064005377697463680048656C6C6F205582 +:100F3000415254312C204543484F2D4D6F647573F9 +:100F4000206163746976650A000A203D3E2073655E +:100F50006E6420746578742076696120554152541E +:100F600031206E6F772E2E2E005561727431000085 +:100F7000DC3D9F3E11241FBECFEFD0E4DEBFCDBFCE +:100F800012E0A0E0B1E0E2E8F5E900E00BBF02C04A +:100F900007900D92A635B107D9F724E0A6E5B2E097 +:100FA00001C01D92A83EB207E1F717E0CAEBD7E0F7 +:100FB00004C02197FE010E94FC40C83BD107C9F73D +:100FC0000E94453C0C94BF4A0C940000FC01108226 +:100FD00011820895FC019181992311F09150918320 +:100FE0000895FC01608384E68093B80084E08093D8 +:100FF000BC0081E0089584E08093BC001092B800AA +:1010000008954150FB0194E824EC4F3FD1F0442374 +:1010100019F02093BC0002C09093BC008091BC00EA +:1010200087FFFCCF8091B900887F442319F0803579 +:1010300019F009C0883539F48091BB008193415083 +:10104000E4CF81E0089580E00895DC01FB0184E8AD +:1010500035E0442389F0222311F0949101C090815E +:101060009093BB008093BC0011963C931197909194 +:10107000BC0097FFFCCF02C081E008959091B900B9 +:1010800041503196987F983221F380E00895FC0119 +:1010900084EA8093BC0085E081838091BC0087FF57 +:1010A000FCCF9091B900987F983011F09031B1F455 +:1010B0009081990F962B9093BB0084E88093BC009D +:1010C00085E081838091BC0087FFFCCF9091B900BF +:1010D000987F611105C081E0983129F080E0089582 +:1010E00081E09034D9F708950F931F93CF93DF9346 +:1010F000D62F8A01C22F60E00E944708811102C0EA +:1011000080E016C0F80124E8D093BB002093BC0017 +:101110009091BC0097FFFCCF9091B900987F9832D6 +:1011200079F7D1919FEF9C0FCC2311F0C92FECCF11 +:10113000DF91CF911F910F91089584E98093BC00B6 +:101140008091BC0084FDFCCF81E00895FF920F9355 +:101150001F93CF93DF93EC018B01F42E61E00E948B +:101160004708882379F04F2DB801CE010E9401086D +:10117000882341F0CE01DF91CF911F910F91FF9015 +:101180000C949D0880E0DF91CF911F910F91FF900B +:101190000895FF920F931F93CF93DF93EC018B0180 +:1011A000F42E60E00E944708882381F020E04F2D54 +:1011B000B801CE010E942508882341F0CE01DF91BD +:1011C000CF911F910F91FF900C949D0880E0DF91CB +:1011D000CF911F910F91FF900895FF920F931F934E +:1011E000CF93DF93EC018B01F42E60E00E9447085F +:1011F000882381F021E04F2DB801CE010E942508FF +:10120000882341F0CE01DF91CF911F910F91FF9084 +:101210000C949D0880E0DF91CF911F910F91FF907A +:101220000895CF92DF92EF92FF920F931F93CF9387 +:10123000DF93EC016B01142F790160E00E944708F5 +:10124000882301F120E0412FB601CE010E9425083C +:101250008823C1F061E0CE010E944708882391F005 +:10126000402FB701CE010E940108882359F0CE011A +:10127000DF91CF911F910F91FF90EF90DF90CF9072 +:101280000C949D0880E0DF91CF911F910F91FF900A +:10129000EF90DF90CF900895FC01108211821282AE +:1012A000138614860895FC019081992311F09150C2 +:1012B0009083089567FD0CC0660F642B6093BA009D +:1012C00084E68093B80085E48093BC0081E00895B3 +:1012D00080E0089584E08093BC001092B8000895E7 +:1012E000FB012FB7F894918181E0890F8183DB01A5 +:1012F000A90FB11D12964C93883008F0118281819C +:101300009081891305C08F5F8083883008F0108238 +:101310002FBF0895462FBC01655F7F4F0C94700965 +:10132000FB019FB7F89480812181821304C09FBF85 +:101330008FEF9FEF089521E0280F2083DB01A80F96 +:10134000B11D12968C91283018F49FBF90E008953B +:101350001082FBCFBC016F5F7F4F0C9490092091EE +:10136000B900287F203819F0283A49F013C040917D +:10137000BB00BC016F5F7F4F0E94700909C0BC01B8 +:10138000655F7F4F0E94900997FD80E08093BB00CE +:1013900085E801C085EC8093BC000895CF93DF936E +:1013A00090915F0480915E049817D1F3E0915F04FF +:1013B00081E08E0F80935F04F0E0E05AFB4FC08124 +:1013C000CD3009F4CAE0D0E06091E4047091E50406 +:1013D000CE010E948946CE01DF91CF9108950F93EF +:1013E0001F93CF93C82F8B018A3019F48DE00E9490 +:1013F000EF098091E4049091E5040817190731F48E +:101400008091C00085FFFCCFC093C60080E090E0D3 +:10141000CF911F910F910895089580E090E0089575 +:101420000895089508950895089508952FB7F8949C +:101430004091900450919104452B59F4429A803880 +:101440003FEF930710F480589F4F9093910480933F +:1014500090042FBF08952FB7F894409192045091B3 +:101460009304452B59F4449A80383FEF930710F4C6 +:1014700080589F4F90939304809392042FBF0895B8 +:10148000813041F0823019F487EF92E0089583EFC4 +:1014900092E008958BEF92E00895CF9387E68093D2 +:1014A0007C0087E880937A0080917A008064809342 +:1014B0007A0080917A0086FDFCCF1092880480919A +:1014C0007900843F10F081E005C080917900803E72 +:1014D00018F082E080938804809188049FEF980F31 +:1014E000923088F0809179001F928F938CEA92E07D +:1014F0009F938F930E94C546109288040F900F907F +:101500000F900F9014C0C09179000E94400A1F9262 +:10151000CF939F938F9386E892E09F938F930E943F +:10152000C5460F900F900F900F900F900F90109254 +:101530007C0010927A0080918804CF910895F894ED +:1015400060938C0470938D0480938E0490938F0429 +:10155000789408950E949F0AF89480918C04909149 +:101560008D04A0918E04B0918F047894892B8A2BDE +:101570008B2B31F08091000190910101019661F374 +:1015800080910001909101010895CF93DF93CFEFF7 +:10159000DFEFD0930101C09300010E94AA0AD0930B +:1015A0000101C0930001DF91CF910895CF93DF93A4 +:1015B000CDB7DEB728970FB6F894DEBF0FBECDBF0C +:1015C0007091800460918104509182044091830461 +:1015D0003091840420918504909186048091870441 +:1015E00079836A835B834C833D832E839F838887C3 +:1015F0008FB7F894E0918004709181046091820427 +:101600005091830440918404309185042091860494 +:1016100090918704E9837A836B835C834D833E8357 +:101620002F8398878FBF29813A814B815C816D819F +:101630007E818F81988528960FB6F894DEBF0FBE05 +:10164000CDBFDF91CF9108951F920F920FB60F92E9 +:1016500011240BB60F922F933F938F939F93EF9389 +:10166000FF938091C600282F30E030930101209332 +:101670000001E0915E0491E09E0F90935E04F0E023 +:10168000E05AFB4F808390915E0480915F04981331 +:1016900005C080915F048F5F80935F04FF91EF919D +:1016A0009F918F913F912F910F900BBE0F900FBE86 +:1016B0000F901F9018951F920F920FB60F92112442 +:1016C0000BB60F922F933F934F935F936F937F933C +:1016D0008F939F93AF93BF93CF93EF93FF93C0915B +:1016E000CE0080912D04882329F06C2F8BE294E0AA +:1016F0000E943E2C80914104882329F06C2F8FE3B7 +:1017000094E00E94713880912804882329F06C2F7E +:1017100086E294E00E946627FF91EF91CF91BF91FE +:10172000AF919F918F917F916F915F914F913F9179 +:101730002F910F900BBE0F900FBE0F901F9018951A +:101740001F920F920FB60F9211240BB60F922F9388 +:101750003F934F935F936F937F938F939F93AF9339 +:10176000BF93EF93FF9380919403882319F084E053 +:1017700093E00DC080910303882319F083E792E082 +:1017800006C080912504882329F085E993E00E9412 +:10179000AF2603C080E88093BC00FF91EF91BF911A +:1017A000AF919F918F917F916F915F914F913F91F9 +:1017B0002F910F900BBE0F900FBE0F901F9018959A +:1017C0001F920F920FB60F9211240BB60F922F9308 +:1017D0003F934F935F936F937F938F939F93AF93B9 +:1017E000BF93EF93FF9380915104882321F08FE4FE +:1017F00094E00E94272380914A04882321F088E402 +:1018000094E00E94952D80918B048F5F8A3018F4AC +:1018100080938B0494C010928B0480918C049091DF +:101820008D04A0918E04B0918F04892B8A2B8B2B71 +:1018300099F080918C0490918D04A0918E04B091C8 +:101840008F040197A109B10980938C0490938D04B2 +:10185000A0938E04B0938F04209180043091810472 +:1018600040918204509183046091840470918504B6 +:101870008091860490918704A1E00E94B940209352 +:1018800080043093810440938204509383046093D6 +:1018900084047093850480938604909387048DE775 +:1018A00093E00E94EA078FE793E00E9453098CEED1 +:1018B00092E00E94EA078EEE92E00E9453098EE0C9 +:1018C00094E00E94EA0780E194E00E94530980912D +:1018D0006A02882329F080916A02815080936A020B +:1018E00080915F02882329F080915F02815080936C +:1018F0005F028091900490919104892B69F080910E +:101900009004909191040197909391048093900496 +:101910008038910508F44298809192049091930444 +:10192000892B69F080919204909193040197909390 +:101930009304809392048038910508F44498809130 +:10194000890490918A04019690938A048093890473 +:101950008838934140F098B188E0892788B910927F +:101960008A0410928904FF91EF91BF91AF919F91EA +:101970008F917F916F915F914F913F912F910F9038 +:101980000BBE0F900FBE0F901F901895CF93DF9353 +:10199000EC01198218828091D50481110EC085ED69 +:1019A00094E00E940D0A892B41F083ED94E00E949F +:1019B000E60785ED94E00E94100A198A1A8A1B8AAC +:1019C0001C8A88E994E09B838A8386E994E09D835E +:1019D0008C8384E994E09F838E8383ED94E09983E4 +:1019E0008883DF91CF910895CF93DF9300D0CDB757 +:1019F000DEB74A83FC0180819181009739F06983C9 +:101A000042E0BE016F5F7F4F0E94C9080F900F90A8 +:101A1000DF91CF910895CF92EF920F93CF93DF9301 +:101A2000EC016295660F660F607C47702770822F0D +:101A3000880F880F880F262F242B282B2FAB029579 +:101A4000000F007E88AD8F71082B08AF8E2D877038 +:101A5000880F880F880F9EA9292F207C9C2D9770B6 +:101A6000E22EE82AE92AE894E7F8EEAA40E064EFDB +:101A7000CE010E94F40C48AD42954695477062EF46 +:101A8000CE010E94F40C9EA9492F477060E2469F48 +:101A900090011124892F869586958695877064E03C +:101AA000869FA0011124422B532B97FB992790F975 +:101AB000492B65EFCE010E94F40C9FA9492F477076 +:101AC00080E2489F90011124892F869586958695FE +:101AD000877064E0869FA0011124422B532B9295BE +:101AE000969596959370492B64EFCE01DF91CF9137 +:101AF0000F91EF90CF900C94F40C0F93CF93DF9352 +:101B00001F92CDB7DEB7FC0180819181009749F02B +:101B1000698301E09E012F5F3F4F41E0B9010E94C0 +:101B2000110989810F90DF91CF910F9108950F9343 +:101B3000CF93DF9300D0CDB7DEB7FC0180819181D8 +:101B4000009749F0698302E09E012F5F3F4F41E01B +:101B5000B9010E94110989819A81982789279827BC +:101B60000F900F90DF91CF910F9108950E94970DE4 +:101B700098278927982708950C94970D0C94B60DF3 +:101B80000F93CF93DF9300D01F92CDB7DEB7FC0148 +:101B900080819181009749F0698303E09E012F5F66 +:101BA0003F4F41E0B9010E94110929816A81862FC6 +:101BB00090E0A0E0B0E0BA2FA92F982F8827A22BA1 +:101BC0002B81BC01CD01622B0F900F900F90DF9104 +:101BD000CF910F9108956F927F928F929F92AF92C3 +:101BE000BF92CF92DF92EF92FF920F931F93CF930A +:101BF000DF93EC019FA9892F807C803409F043C0DA +:101C0000492F477060E2469F90011124892F8695E5 +:101C1000869586958770E4E08E9FA0011124422B63 +:101C2000532B9295969596959370492B64EFCE0120 +:101C30000E94F40C0E94D60A922E832E742E652EDA +:101C400063EFCE010E947D0D83FF1FC00E94D60A64 +:101C5000A92CB82CC72CD62CE12CF12C00E010E0DC +:101C60000E94C540203D3740410551056105710581 +:101C70008105910509F038F487EB9BE00197F1F7B6 +:101C800000C00000DDCF80E001C081E0DF91CF9196 +:101C90001F910F91FF90EF90DF90CF90BF90AF908A +:101CA0009F908F907F906F9008951F93CF93DF93B5 +:101CB000EC0168E80E94B60D9E8B8D8B6AE8CE0120 +:101CC0000E94B60D988F8F8B6CE8CE010E94B60DE6 +:101CD0009A8F898F6EE8CE010E94B60D9C8F8B8FF4 +:101CE00060E9CE010E94B60D9E8F8D8F62E9CE0114 +:101CF0000E94B60D98A38F8F64E9CE010E94B60DA5 +:101D00009AA389A366E9CE010E94B60D9CA38BA37A +:101D100068E9CE010E94B60D9EA38DA36AE9CE01AB +:101D20000E94B60D98A78FA36CE9CE010E94B60D54 +:101D30009AA789A76EE9CE010E94B60D9CA78BA732 +:101D400061EACE010E947D0D8DA761EECE010E9459 +:101D5000B60D9FA78EA763EECE010E947D0D88ABC6 +:101D600064EECE010E947D0D182F65EECE010E941B +:101D70007D0D90E11902900111248F70282B3AAB50 +:101D800029AB66EECE010E947D0D182F65EECE01C7 +:101D90000E947D0D90E119029001112490E044E031 +:101DA000959587954A95E1F7822B932B9CAB8BAB4E +:101DB00067EECE010E947D0D8DABDF91CF911F911B +:101DC000089563EF0E947D0D81700895CF92DF9298 +:101DD000EF92FF920F931F93CF93DF93EC0160ED8F +:101DE0000E947D0D90E0A0E0B0E089879A87AB87E4 +:101DF000BC8780369105A105B10569F546EB60EE1B +:101E0000CE010E94F40C8FE295E70197F1F700C034 +:101E10000000CE010E94E10E8111F5CFCE010E949B +:101E2000550EC12CD12CE12CF12C05E010E025E061 +:101E300030E045E050E063E070E0CE010E940B0D21 +:101E40009FE729EA83E0915020408040E1F700C0FD +:101E5000000081E001C080E0DF91CF911F910F91E0 +:101E6000FF90EF90DF90CF900895CF93DF93EC0138 +:101E7000888199810E94F107882329F0CE01DF91A2 +:101E8000CF910C94E60E80E0DF91CF9108954F92B0 +:101E90005F926F927F928F929F92AF92BF92CF92FA +:101EA000DF92EF92FF92CF93DF93EC016AEF0E94F3 +:101EB000C00D6115710520E88207910509F485C000 +:101EC0006B017C0184E0F594E794D794C7948A95DC +:101ED000D1F74D885E88C701B60128E030E040E0C8 +:101EE00050E00E94173F612C712CD301C201880F72 +:101EF000991FAA1FBB1F281B390B4A0B5B0BAF890D +:101F0000B88D0E94413F4B015C01C701B60120E141 +:101F100030E040E050E00E94173FCA01B901641967 +:101F20007509860997099B01AC010E94D93E9B0166 +:101F3000AC017CE055954795379527957A95D1F773 +:101F4000A98DBA8D0E94413F2B013C01C501B4010E +:101F500020E038E040E050E00E94173F69017A013C +:101F6000C301B20120E030E440E050E00E94173F9E +:101F7000BA01A9014C0D5D1D6E1D7F1D89899A89CD +:101F8000AB89BC899A01AB01280F391F4A1F5B1F1F +:101F90002D873E874F87588BA5E0B0E00E94363FE3 +:101FA00060587F4F8F4F9F4F20E031E040E050E07E +:101FB0000E94173FCA01B9010E94314220E030E07F +:101FC00048EC52E40E94814104C060E070E080EC83 +:101FD0009FE7DF91CF91FF90EF90DF90CF90BF9080 +:101FE000AF909F908F907F906F905F904F900895EB +:101FF0002F923F924F925F926F927F928F929F9219 +:10200000AF92BF92CF92DF92EF92FF920F931F9306 +:10201000CF93DF93CDB7DEB76D970FB6F894DEBFE1 +:102020000FBECDBF1C010E94470F67EFC1010E9488 +:10203000C00D6F87788B898B9A8B611571058048ED +:10204000910509F49AC2F10185859685A785B08925 +:102050006C017D01FF0CCC08DC2C76019C01AD01EC +:102060006C2D7C2D8C2D9C2D345F414051096109D4 +:1020700071098109910929873A874B875C873B0160 +:102080004C0159016A017B018C010E94493F422E9B +:102090003D874E875B8B6C8B7D8B8E8B9B8FD101AD +:1020A0005E963C915E975D962C91932F990F990BBC +:1020B000492F592F692F792F892F0E94493F0CE012 +:1020C0000E9479402C8F3D8F49835F8B688F798F79 +:1020D000582E9A8FF10130A1278D932F990F990BCC +:1020E000A42CBD84CE84DB88EC88FD880E891B8DF2 +:1020F000492F592F692F792F892F0E94493FF22E9E +:10210000032F142FB52FF62FE72FD82EE92EA0E09E +:102110000E94CE4084F421503F4F4F4F5F4F6F4F8E +:102120007F4F8F4F9F4FF22E032F142FB52FF62F77 +:10213000E72FD82EE92E2F2D302F412F5B2F6F2F19 +:102140007E2F8D2D9E2D08E00E949240AC8CBD8C80 +:10215000C980DF88E88CF98C052D1A8D0E94B0406B +:1021600070588F4F9F4FD1015B96ED91FC915C971A +:102170005F01C12CD12CE12CF12C8701E983BA82BB +:10218000CB82DC82ED82FE820F831887C12CD12C9A +:10219000E12CF12C00E010E00E94493F122F3F8B10 +:1021A0004983B52FF62FE72FF82E092FA0E00E94C4 +:1021B000CE4084F421503109410951096E4F7F4FBF +:1021C0008F4F9F4F122F3F8B4983B52FF62FE72F4D +:1021D000F82E092F212F3F8949815B2F6F2F7E2FEA +:1021E0008F2D902F01E20E9492402D8F532E498314 +:1021F0005F8B688F798F8A8F9C8FA0E00E94CE4082 +:1022000009F4C0C1D10196963C91969795962C9170 +:10221000932F990F990BA42CBD84CE84DB88EC8876 +:10222000FD880E891B8D492F592F692F792F892FF2 +:102230000E94493F2D873E87442E5B8B6C8B7D8BA4 +:102240008E8B9B8FF10134A123A1932F990F990BB2 +:10225000A984BA84CB84DC8473018401492F592F6B +:10226000692F792F892F0E94493F01E10E9479400F +:10227000AD84BE84C42CDB88EC88FD880E891B8D60 +:102280000E94B04059016A017B01482E192F4F85E9 +:10229000588969897A8934E0759567955795479586 +:1022A0003A95D1F780E090E0A0E1B0E0841B950B77 +:1022B000A60BB70B3C014D01990C6608762C430127 +:1022C0009C01AD01662D762D862D962D0FE10E9485 +:1022D0007940722E832E942E652EB62FA72F8A87D3 +:1022E0009B87F10132A121A1932F990F990B492FBF +:1022F000592F692F792F892F03E20E947940042DED +:102300000E94B04059016A017B018C01272D382DB4 +:10231000492D562D6B2F7A2F8A859B850E94C540AB +:10232000E5E3AE2EFCE0BF2EC12CD12CE12CF12C2C +:1023300000E010E00E94493FAD8CB52CC980DF88D9 +:10234000E88CF98C0A8D1C8D0E94A43F2C8B3B8B52 +:102350002A013B014C01F22E032F142FB52DF62F2D +:10236000E72DD82EE92CA0E00E94CE4084F4215025 +:10237000304E4F4F5F4F6F4F7F4F8F4F9F4FF22E1B +:10238000032F142FB52FF62FE72FD82EE92E2F2D40 +:10239000302F412F5B2F6F2F7E2F8D2D9E2D0DE027 +:1023A0000E94924029873A874B875C876F877983A1 +:1023B0008D879E87D1019C963C919C979B962C91F2 +:1023C000932F990F990BA984BA846A017B01082F76 +:1023D0001E85492F592F692F792F892F0E94493F38 +:1023E0000E94493FF22E032F142FB52FF62FE72F0F +:1023F000D82EE92EA0E00E94CE4084F4215031096D +:1024000041095E4F6F4F7F4F8F4F9F4FF22E032F2B +:10241000142FB52FF62FE72FD82EE92E2F2D302F82 +:10242000412F5B2F6F2F7E2F8D2D9E2D09E10E9456 +:102430009240AC88BB886201730184010E94B04065 +:1024400029873A874B875C876F8779838D879E873B +:10245000F10132A521A5932F990F990B492F592FDF +:10246000692F792F892F0E94493F122F3C8B4B8B6C +:10247000B52FF62FE72FF82E092FA0E00E94CE40AF +:1024800084F421503109484F5F4F6F4F7F4F8F4F7A +:102490009F4F122F3C8B4B8BB52FF62FE72FF82E2B +:1024A000092F212F3C894B895B2F6F2F7E2F8F2D7A +:1024B000902F03E10E94924059016A017B018C0137 +:1024C00029853A854B855C856F8579818D859E85CB +:1024D0000E94B040122F39874983B52FF62FE72F7E +:1024E000F82E092FA0E00E94CE4084F421503F4FE7 +:1024F0004F4F5F4F6F4F7F4F8F4F9F4F122F398737 +:102500004983B52FF62FE72FF82E092F212F398574 +:1025100049815B2F6F2F7E2F8F2D902F08E00E9417 +:1025200092402983F32F6A017B01E82F192FD101F3 +:1025300097968D919C919897092E000CAA0BBB0B36 +:102540004C015D01BB0C8808982C54019C01AD0125 +:10255000682D782D882D982D04E00E947940A9805F +:10256000BF2E0E2F0E94B0400E94294220E030E092 +:1025700040E85BE30E940B4308C060E070E080EC41 +:102580009FE703C060E070E0CB016D960FB6F89452 +:10259000DEBF0FBECDBFDF91CF911F910F91FF9096 +:1025A000EF90DF90CF90BF90AF909F908F907F90F3 +:1025B0006F905F904F903F902F9008954F925F9251 +:1025C0006F927F928F929F92AF92BF92CF92DF9243 +:1025D000EF92FF92CF93DF9300D000D000D0CDB721 +:1025E000DEB79E838D830E94470F6DEF8D819E81A4 +:1025F0000E94970DA0E0B0E0811520E89207A105A8 +:10260000B10509F4D3C0ED81FE8185849684A78449 +:10261000B088FCE29F1AF1E0AF0AB1083EE0880FF3 +:10262000991FAA1FBB1F3A95D1F7ED81FE8141A9E1 +:1026300052A9052E000C660B770B24E1440F551FA1 +:10264000661F771F2A95D1F76C017D01C41AD50A40 +:10265000E60AF70AA3A9B4A9A50194010E94413F83 +:10266000A7019601261B370B480B590BCA01B9016C +:10267000705C8F4F9F4F412C30E8532E612C712C92 +:10268000A30192010E94173F29833A834B835C8305 +:10269000ED81FE81A0A9B0E0A50194010E94363F22 +:1026A00020E038E040E050E00E94173F69017A01E5 +:1026B000F0E8DF0EE11CF11CED81FE81A5A90A2ED8 +:1026C000000CBB0BA50194010E94413F812C44E00A +:1026D000942EA12CB12CA50194010E94173FCA0190 +:1026E000B901A70196010E94D93EA50194010E945B +:1026F000173F405E5F4FED81FE81A6A5B7A50E9402 +:10270000413F705E8F4F9F4F20E030E440E050E04B +:102710000E94173F69817A818B819C810E94D93EFA +:102720006B017C01A30192010E94173FCA01B9010C +:102730000E94D93E9B01AC0167E0559547953795BE +:1027400027956A95D1F7ED81FE81A5A5B0E00E949D +:10275000363F20E130E040E050E00E94173FD701D3 +:10276000C601821B930BA40BB50BB7FF03C080E01F +:1027700090E0DC0181309105A105F9E1BF0724F06B +:1027800080E090E0A0E0B9E1BC01CD012CE095959E +:102790008795779567952A95D1F70E942F4220E07B +:1027A00030E040E85AE30E940B4304C060E070E070 +:1027B00080EC9FE726960FB6F894DEBF0FBECDBF24 +:1027C000DF91CF91FF90EF90DF90CF90BF90AF90CF +:1027D0009F908F907F906F905F904F900895CF92D1 +:1027E000DF92EF92FF926A017B010E94F80F20E0D6 +:1027F00030E048EC52E40E948141A70196010E941A +:10280000814121E03EED42E45EE30E9478439B017A +:10281000AC0160E070E080E89FE30E94144120E09A +:102820003AE24DE257E40E940B43FF90EF90DF90B5 +:10283000CF900895CF92DF92EF92FF920F931F9364 +:10284000CB01BA016801790120E03AE24DE257E498 +:102850000E9481419B01AC0160E070E080E89FE351 +:102860000E94144126EF38E248EA50E40E9478437F +:102870009B01AC01C701B6010E9481411F910F91DC +:10288000FF90EF90DF90CF900895FC0161857285F5 +:10289000838594850895FC0121893289438954896F +:1028A000A5E0B0E00E94363F672F782F892F992747 +:1028B00087FD9A950E94314220E030E048EC52E4D6 +:1028C0000E9481410895CF93DF93EC01CB01BA01BF +:1028D00020E030E048EC52E40E940B430E94F341B8 +:1028E000982F872F762F662725E030E040E050E0D4 +:1028F0000E94173F298B3A8B4B8B5C8BDF91CF91DA +:1029000008950F931F93CF93DF938C01EB0188E21F +:10291000FB0111928A95E9F74BE050E06BE771E01B +:10292000CE010E9420461B8681E090E0A0E0B0E04E +:102930008C879D87AE87BF87F80180819181092EA2 +:10294000000CAA0BBB0B888B998BAA8BBB8B8DE0E1 +:1029500090E0A0E0B0E08C8B9D8BAE8BBF8B1CA277 +:102960001DA21EA21FA280E090E0A0E2B2EC8C8F1C +:102970009D8FAE8FBF8F80E090E0AAEAB2E4888F8F +:10298000998FAA8FBB8F8AE097EDA3E2BCE388A35F +:1029900099A3AAA3BBA3DF91CF911F910F91089593 +:1029A000CF93DF93EB0124E2FB0111922A95E9F723 +:1029B00044E250E060E070E0488359836A837B839F +:1029C000FC0180819181092E000CAA0BBB0B8C832A +:1029D0009D83AE83BF838DE090E0A0E0B0E0888768 +:1029E0009987AA87BB870E94D60A288B398B4A8B86 +:1029F0005B8B8AE994E00E94470F6C8B7D8B8E8BFA +:102A00009F8B81E0DF91CF9108950F931F93CF9318 +:102A1000DF938C01EB0188E2FB0111928A95E9F7C3 +:102A20004BE050E06BE771E0CE010E9420461B8630 +:102A300081E090E0A0E0B0E08C879D87AE87BF8703 +:102A4000F80180819181092E000CAA0BBB0B888BA9 +:102A5000998BAA8BBB8B86E090E0A0E0B0E08C8BDA +:102A60009D8BAE8BBF8B1CA21DA21EA21FA280E05D +:102A700090E0A6E9B3E48C8F9D8FAE8FBF8F80E08E +:102A800090E8A9E8B4E4888F998FAA8FBB8F86EA73 +:102A90009BE9A4E4BCE388A399A3AAA3BBA3DF9109 +:102AA000CF911F910F910895CF93DF93EB0124E213 +:102AB000FB0111922A95E9F744E250E060E070E0F2 +:102AC000488359836A837B83FC0180819181092E2D +:102AD000000CAA0BBB0B8C839D83AE83BF8386E067 +:102AE00090E0A0E0B0E088879987AA87BB870E9422 +:102AF000D60A288B398B4A8B5B8B8AE994E00E943B +:102B0000F80F20E030E048EC52E40E9481416C8BE9 +:102B10007D8B8E8B9F8B81E0DF91CF9108950F93FA +:102B20001F93CF93DF938C01EB0188E2FB0111929D +:102B30008A95E9F74BE050E06BE771E0CE010E9427 +:102B400020461B8681E090E0A0E0B0E08C879D8766 +:102B5000AE87BF87F80180819181092E000CAA0BF6 +:102B6000BB0B888B998BAA8BBB8B8CE090E0A0E091 +:102B7000B0E08C8B9D8BAE8BBF8B1CA21DA21EA2C6 +:102B80001FA21C8E1D8E1E8E1F8E80E090E0A8EC72 +:102B9000B2E4888F998FAA8FBB8F80E090E0A0E489 +:102BA000B0E488A399A3AAA3BBA3DF91CF911F91FF +:102BB0000F910895CF93DF93EB0124E2FB01119273 +:102BC0002A95E9F744E250E060E070E048835983D9 +:102BD0006A837B83FC0180819181092E000CAA0B02 +:102BE000BB0B8C839D83AE83BF838CE090E0A0E021 +:102BF000B0E088879987AA87BB870E94D60A288B6E +:102C0000398B4A8B5B8B8AE994E00E94DE126C8BD5 +:102C10007D8B8E8B9F8B81E0DF91CF910895CF9339 +:102C2000DF93EC010E94E6071A828CE78CAB8AE006 +:102C30008DAB8EE78EAB8FEA8FAB88AF82EA89AF20 +:102C40001AAE80E88BAFDF91CF910895CF93DF93D9 +:102C500000D0CDB7DEB74A83698342E0BE016F5F23 +:102C60007F4F0E94C9080F900F90DF91CF91089578 +:102C70000F93CF93DF931F92CDB7DEB79A0169838D +:102C800001E041E0BE016F5F7F4F0E9411090F908C +:102C9000DF91CF910F9108950F93CF93DF931F9200 +:102CA000CDB7DEB79A01698302E041E0BE016F5FF4 +:102CB0007F4F0E9411090F90DF91CF910F910895DE +:102CC0000F931F93CF93DF9300D0CDB7DEB78A0168 +:102CD000AE014F5F5F4F0E944C16882341F029815F +:102CE0003A81322723273227F801318320830F903E +:102CF0000F90DF91CF911F910F9108950F93CF9374 +:102D0000DF931F92CDB7DEB76983022F9E012F5F3D +:102D10003F4F41E0B9010E9411090F90DF91CF911F +:102D20000F91089540EF60E10E942616EFE2F5E76B +:102D30003197F1F700C0000008950F931F93CF93D0 +:102D4000DF9300D0CDB7DEB78C01AE014F5F5F4F90 +:102D500060E00E944C168FE295E70197F1F700C002 +:102D6000000089819A818036E1E09E0721F4F80114 +:102D7000128281E008C08136914021F481E0F8019F +:102D8000828301C080E00F900F90DF91CF911F915F +:102D90000F9108950F931F93CF93DF931F92CDB799 +:102DA000DEB78C0140E062E10E942616811108C066 +:102DB0008FE295E70197F1F700C0000080E016C0B0 +:102DC0004CEC62E1C8010E942616882389F38FE249 +:102DD00095E70197F1F700C00000AE014F5F5F4F2C +:102DE00060E2C8010E943816882311F30F90DF912A +:102DF000CF911F910F9108950F931F93CF93DF935E +:102E000000D01F92CDB7DEB78C010E94CA1688236E +:102E100059F18FE295E70197F1F700C000004EE00D +:102E200062E1C8010E9426168823F1F023E0AE017A +:102E30004F5F5F4F6CE4C8010E947E168823A1F0AB +:102E40002981F80123839A8194839B81958391E062 +:102E5000273008F490E0F8019283EFE2F5E731972C +:102E6000F1F700C0000007C08FE295E70197F1F786 +:102E700000C0000080E00F900F900F90DF91CF9185 +:102E80001F910F910895633021F4FC0122812223C8 +:102E900059F0462F60E10E942616EFE2F5E73197E0 +:102EA000F1F700C00000089580E00895CF93DF930C +:102EB000EC0163E50E94F1078FE295E70197F1F7D6 +:102EC00000C00000CE010E9492168823A9F0CE0116 +:102ED0000E949D16882381F061E0CE010E94431775 +:102EE000882351F0CE010E94CA16882329F0CE0112 +:102EF000DF91CF910C94FC1680E0DF91CF91089583 +:102F0000CF93DF9361157105D9F0EC017F836E8358 +:102F100061E00E944317882399F0CE010E94CA16EF +:102F2000882371F042EC62E1CE010E9426168823CC +:102F300039F0EFE2F5E73197F1F700C0000007C084 +:102F40008FE295E70197F1F700C0000080E0DF9184 +:102F5000CF9108956F927F928F929F92AF92BF927E +:102F6000CF92DF92EF920F931F93CF93DF931F9235 +:102F7000CDB7DEB73C01CB01342FE02FAFE2B5E790 +:102F80001197F1F700C0000068E170E00E94033F74 +:102F90008B014FEF460F4295440F440F407C311197 +:102FA000406221114061E1114860E110446060E439 +:102FB000C3010E942616882309F471C0A80141505C +:102FC0005109569547955695479561E4C3010E946E +:102FD0002616882309F463C0A6015695479562E436 +:102FE000C3010E942616882309F459C0A5015695ED +:102FF000479563E4C3010E942616882309F44FC055 +:10300000A4015695479564E4C3010E9426168111D8 +:1030100046C049895A895695479565E4C3010E947F +:10302000261681113CC0F3014681415066E4C3017C +:103030000E942616811133C0D30116968D919C9162 +:103040001797019711F440E801C040E067E4C3011D +:103050000E942616882319F1EFE2F5E73197F1F780 +:1030600000C00000AE014F5F5F4F6FE4C3010E94DC +:1030700038168823A1F0AFE2B5E71197F1F700C049 +:103080000000F30126813781B901606C498150E06D +:103090006417750729F02F5F3F4F3783268380E041 +:1030A0000F90DF91CF911F910F91EF90DF90CF9014 +:1030B000BF90AF909F908F907F906F900895AB01DD +:1030C00060E20C9438160F93CF93DF931F92CDB725 +:1030D000DEB79B0141E2498305E041E0BE016F5F3D +:1030E0007F4F0E9411090F90DF91CF910F910895AA +:1030F0000F931F93CF93DF93CDB7DEB729970FB60A +:10310000F894DEBF0FBECDBF8C016623F1F0AE0197 +:10311000475F5F4F60E2C8010E9438168823E9F0DC +:1031200087EB9BE00197F1F700C00000898581FFE4 +:10313000EECF898581FF11C027E0AE014F5F5F4F61 +:1031400061E2C8010E947E1608C0AE01475F5F4F72 +:1031500060E20E9438168111ECCF80E029960FB60C +:10316000F894DEBF0FBECDBFDF91CF911F910F91BD +:1031700008950F931F93CF93DF93CDB7DEB72997B1 +:103180000FB6F894DEBF0FBECDBF8C016623C1F031 +:1031900087EB9BE00197F1F700C00000AE01475FAD +:1031A0005F4F60E2C8010E943816811102C080E0C2 +:1031B0000AC1898580FFECCF898580FFF8CF09C0DF +:1031C000AE01475F5F4F60E20E9438168111F4CF75 +:1031D000EECF28E0AE014F5F5F4F68E4C8010E9468 +:1031E0007E16882321F369817A8180E090E00E9435 +:1031F0002F4220E030E040E05AE30E940B439B0165 +:10320000AC0160E070E080E090E40E9478430E94AE +:10321000FA41F8016787708B818B928B6B817C817F +:1032200080E090E00E942F4220E030E040E05AE34E +:103230000E940B439B01AC0160E070E080E090E4F1 +:103240000E9478430E94FA41F801678B708F818F4A +:10325000928F6D817E8180E090E00E942F4220E07D +:1032600030E040E05AE30E940B439B01AC0160E078 +:1032700070E080E090E40E9478430E94FA41F801F7 +:10328000678F70A381A392A36F81788580E090E01F +:103290000E942F4220E030E040E05AE30E940B43BE +:1032A0009B01AC0160E070E080E090E40E94784314 +:1032B0000E94FA41F80167A370A781A792A728E0AE +:1032C000AE014F5F5F4F68E2C8010E947E168823FF +:1032D00009F46DCF69817A8180E090E00E942F42ED +:1032E00020E030E040E05AE30E940B439B01AC0138 +:1032F00060E070E080E090E40E9478430E94FA4130 +:10330000F801638B748B858B968B6B817C8180E05D +:1033100090E00E942F4220E030E040E05AE30E941B +:103320000B439B01AC0160E070E080E090E40E9400 +:1033300078430E94FA41F801638F748F858F968FCE +:103340006D817E8180E090E00E942F4220E030E09D +:1033500040E05AE30E940B439B01AC0160E070E047 +:1033600080E090E40E9478430E94FA41F80163A350 +:1033700074A385A396A36F81788580E090E00E9476 +:103380002F4220E030E040E05AE30E940B439B01D3 +:10339000AC0160E070E080E090E40E9478430E941D +:1033A000FA41F80163A774A785A796A7AE014F5FFE +:1033B0005F4F68E3C8010E943816882309F4F7CEEE +:1033C0009981F80193AB29960FB6F894DEBF0FBE32 +:1033D000CDBFDF91CF911F910F910895CF93DF93D0 +:1033E00000D000D0CDB7DEB769837A834B835C838E +:1033F00024E0AE014F5F5F4F63E10E9474080F90BD +:103400000F900F900F90DF91CF9108958F929F9220 +:10341000AF92BF92CF92DF92EF92FF920F931F93E2 +:10342000CF93DF93EC014A015B01C901B80120E0B1 +:1034300030E040E054E40E940B430E94FA416B01EB +:103440007C0123E333E948E853E4C501B4010E9459 +:10345000154120E030E040E852E40E940B430E9416 +:10346000FA41A601CE01DF91CF911F910F91FF90FC +:10347000EF90DF90CF90BF90AF909F908F900C9483 +:10348000EE19FC01878190858330910540F4880F07 +:10349000991FFC01EC59FE4F8081918108958EE1C6 +:1034A00095E00895289820981CBC2C9824B12F7082 +:1034B00024B921B12F7C21B922B12F7C22B93E98A9 +:1034C0004698FC011682089560E070E0CB010E94EE +:1034D000AA0A0196C9F30895611102C00E94641AF4 +:1034E0008FEF08956EBD0DB407FEFDCF8EB5089524 +:1034F000289A08952898089595B181E0892785B97B +:10350000089514980895149A089583B186FB882726 +:1035100080F9089580B185FB882780F908952AE015 +:10352000FC0121871E9B06C0FC0121852111FACFD9 +:1035300080E0089581E0089524E6FC012187059943 +:1035400006C0FC0121852111FACF80E0089581E0B9 +:10355000089524E6FC012187059B06C0FC01218516 +:103560002111FACF80E0089581E008950F931F9311 +:10357000CF93DF93EC01162F0E94811ACE010E9497 +:103580008F1A082F612F6F73CE010E94721ACE011D +:103590000E94831A802FDF91CF911F910F91089580 +:1035A000FF920F931F93CF93DF93EC01062FF42E1E +:1035B0000E94811ACE010E948F1A182F602FCE010F +:1035C0000E94721A6F2DCE010E94721A8A838D8119 +:1035D00081608D83CE010E94831A8A8580FF2BC073 +:1035E0001F92FF921F920F9384EA94E09F938F9310 +:1035F0000E94C5460F900F900F900F900F900F9064 +:10360000112389F08D8182FF0EC08A811F928F93D2 +:1036100085E994E09F938F930E94C5460F900F9089 +:103620000F900F9008C080E994E09F938F930E94C1 +:10363000C5460F900F90812FDF91CF911F910F9171 +:10364000FF900895AF92BF92CF92DF92EF92FF92D8 +:103650000F931F93CF93DF93EC01B42ED52E022F3F +:103660006F73162F10640E94811ACE010E948F1A68 +:10367000C82E612FCE010E94721AEB2CFD2C57012F +:10368000D02EDD2041F0F50161915F01CE010E9455 +:10369000721ADA94F6CFCE010E94831A8A8581FFCE +:1036A00046C01F731F921F9384E794E09F938F93EC +:1036B0000E94C5460F900F900F900F90102F8CE630 +:1036C000A82E84E0B82E112379F0F70181917F01B3 +:1036D0001F928F93BF92AF920E94C54611500F90D8 +:1036E0000F900F900F90EFCF87E694E09F938F930A +:1036F0000E94C5460F900F90CC2089F08D8182FFEB +:103700000EC08A811F928F9388E594E09F938F93D8 +:103710000E94C5460F900F900F900F9008C083E550 +:1037200094E09F938F930E94C5460F900F908C2D2D +:10373000DF91CF911F910F91FF90EF90DF90CF908D +:10374000BF90AF900895EF92FF920F931F93CF9386 +:10375000DF93EC017A016F73162F10680E94811AB3 +:10376000CE010E948F1A082F612FCE010E94721A7B +:103770008A838D8181608D8360E0CE010E94721A00 +:10378000F7018083CE010E94831A8A8582FF2EC0B2 +:103790001F731F921F9387E394E09F938F930E9460 +:1037A000C5460F900F900F900F900023B9F08D81B8 +:1037B00080FF14C08A811F928F93F70180811F922E +:1037C0008F9381E294E09F938F930E94C5460F9060 +:1037D0000F900F900F900F900F9008C08CE194E025 +:1037E0009F938F930E94C5460F900F90802FDF917B +:1037F000CF911F910F91FF90EF900895AF92BF92DC +:10380000CF92DF92EF92FF920F931F93CF93DF93AC +:103810008C01D62FB42ED52EC22F0E94811AC8013A +:103820000E948F1AC82E6D2F606CC8010E94721AF8 +:10383000EB2CFD2C5701DC2EDD2049F060E0C801A7 +:103840000E94721AF50181935F01DA94F5CFC801E5 +:103850000E94831AF801828583FF31C01F92CF93A3 +:10386000DF731F92DF9385EF93E09F938F930E9406 +:10387000C5460F900F900F900F900F900F90CC2097 +:10388000B1F00DEE13E0CC2379F0F70181917F01C7 +:103890001F928F931F930F930E94C546C1500F90A4 +:1038A0000F900F900F90EFCF8BEE93E002C086EE5B +:1038B00093E09F938F930E94C5460F900F908C2D9D +:1038C000DF91CF911F910F91FF90EF90DF90CF90FC +:1038D000BF90AF90089521E00C94FE1B9B012053F4 +:1038E00031092E30310510F40C946B1C80E00895E2 +:1038F000AF92BF92CF92DF92EF92FF920F931F93FE +:10390000CF93DF93CDB7DEB762970FB6F894DEBFE3 +:103910000FBECDBF8C01242F8AE0F8018187AB0157 +:103920006FE3C8010E94221BF82EAE014E5E5F4F6E +:103930006AE370E0C8010E946B1C9A899431A1F57A +:103940008F2191F165E370E0C8010E94B61AF82E4C +:10395000882351F1D12CE12CAE014F5E5F4F65E31E +:1039600070E0C8010E946B1CF8228989D81659F0B2 +:10397000FFE0FE1540F0E1E0F0E0EC0FFD1FEE0D82 +:10398000F11D8083E394FF2049F0833129F0F80191 +:103990009185D82E9111E0CFFF24F39491E08331EB +:1039A00009F090E0F92241F56BE370E0C8010E9454 +:1039B000B61A80E009C082ED93E09F938F930E9436 +:1039C000C5460F900F9081E0817062960FB6F89413 +:1039D000DEBF0FBECDBFDF91CF911F910F91FF9042 +:1039E000EF90DF90CF90BF90AF900895222389F0A1 +:1039F000F8018185882369F0AE014E5E5F4F6AE36E +:103A000070E0C8010E946B1C2A898111EFCF80E011 +:103A100001C081E091E0211190E0892329F2AE01FB +:103A20004F5E5F4F65E370E0C8010E946B1C2989FF +:103A3000882339F0213039F0F80181858111EFCFE9 +:103A400002C080E001C081E091E0213009F090E007 +:103A5000892309F4B9CF8DED93E09F938F930E9452 +:103A6000C546CE0101965C010F900F908C0185ED4B +:103A7000C82E83E0D82E802F8A198E1508F09BCF90 +:103A8000F80181918F011F928F93DF92CF920E9454 +:103A9000C5460F900F900F900F90EDCF3F924F9231 +:103AA0005F926F927F928F929F92AF92BF92CF92CE +:103AB000DF92EF92FF920F931F93CF93DF93CDB7D7 +:103AC000DEB767970FB6F894DEBF0FBECDBF6C01AF +:103AD000611105C046E062E00E94D01A14C1623054 +:103AE00008F010C18BE796E09F938F930E94C54624 +:103AF0000F900F9080E090E0EE24EA94FE2C512C81 +:103B000034E1432E4BE0642E46E0742E55E0852EC2 +:103B100056E0952E2FEF30E03E8B2D8B5C013FEF72 +:103B2000A31AB30A9F938F9389E396E09F938F9391 +:103B30000E94C546AE014B5E5F4F65E370E0C60173 +:103B40000E946B1C0F900F900F900F90811109C075 +:103B500026E336E03F932F930E94C5460F900F90C7 +:103B60000EC08E898F938D898F93EFE2F6E0FF93DD +:103B7000EF930E94C5460F900F900F900F908F8982 +:103B800089831F925F921F924F9281E196E09F93EB +:103B90008F930E94C5468E010E5F1F4F0F900F90AE +:103BA0000F900F900F900F90312C832D8A0DF801FC +:103BB00081938F011F928F937F926F920E94C546CF +:103BC00033940F900F900F900F90F3E13F12EDCFD1 +:103BD0009F928F920E94C54644E1BE016F5F7F4F66 +:103BE000C6010E94781C0F900F90811171C084E66D +:103BF00090E00E94160A82E096E09F938F930E94C5 +:103C0000C5460F900F902FEFE216F20609F45EC042 +:103C10003BE2E316F10429F08DE2E816F10431F0FD +:103C20000CC091E0951558F45A940EC0552051F0EF +:103C3000E9E0E51548F0539407C0512C05C0552420 +:103C4000539402C08AE0582EF5E05F9E8001112453 +:103C50000951194F41E050E0B801CE0147960E944A +:103C6000FE4588E696E09F938F930E94C5460F5FBE +:103C70001F4F1F930F930E94C54683E696E09F93C4 +:103C80008F930E94C5464F896EE3C6010E94D01AE9 +:103C90000F900F900F900F900F900F90882371F05E +:103CA0008F891F928F9380E596E09F938F930E9458 +:103CB000C5460F900F900F900F9008C08AE496E0D1 +:103CC0009F938F930E94C5460F900F90C50122CFFE +:103CD00084E690E00E942B0A60ED77E080E090E0BF +:103CE0000E94C50A7C012FEF35E03F932F930E947D +:103CF000C5460F900F909BE1E916F10409F083CFC0 +:103D000080E001C08FEF67960FB6F894DEBF0FBE5C +:103D1000CDBFDF91CF911F910F91FF90EF90DF907A +:103D2000CF90BF90AF909F908F907F906F905F905B +:103D30004F903F9008958F929F92AF92BF92CF92F3 +:103D4000DF92EF92FF920F931F93CF93DF9300D0F8 +:103D5000CDB7DEB78C014B015A01C22EFA01108299 +:103D600082E3F801818785818B7F8583AE014E5F79 +:103D70005F4F6BE370E0C8010E946B1CF82E882334 +:103D800009F446C08A818F7709F446C0D82E8FEA9D +:103D900094E00197F1F700C00000AE014E5F5F4F65 +:103DA0006BE370E0C8010E946B1C882321F09A81AC +:103DB0009F77E92E01C0ED2CF801218591E02111BA +:103DC00001C090E0F92EF82219F1DE1411F0DE2C7A +:103DD000DECFF80185818270D82E90E044962E2D9A +:103DE00030E08217930769F19F938F93F5018081EB +:103DF0001F928F9387EA93E09F938F930E94C5460B +:103E0000F50110820F900F900F900F900F900F9070 +:103E1000F12C02C0E11056C06AE370E0C8010E94B4 +:103E2000B61A8F210F900F90DF91CF911F910F91B4 +:103E3000FF90EF90DF90CF90BF90AF909F908F90CA +:103E400008959F938F9380E993E09F938F930E94AF +:103E5000C5460F900F900F900F90F4E1FC1518F4E9 +:103E60002E2D2D1901C02C2DF5012083A4016FEFFB +:103E7000C8010E94FE1BF501811102C01082C8CF4B +:103E80008081E81ADE1430F686E793E09F938F93E3 +:103E90000E94C5460F900F90AE014F5F5F4F6FEFCE +:103EA000C8010E94A31BEA94DE14B0F3DD2009F4DC +:103EB000AFCF22E0A8014D5F5F4F6FEFC8010E94B6 +:103EC000FE1BA6CF22E0A8014D5F5F4F6FEFC80138 +:103ED0000E94FE1BF82E882309F49ACFF8018581F1 +:103EE0008460858399CF3F924F925F926F927F92C9 +:103EF0008F929F92AF92BF92CF92DF92EF92FF92FA +:103F00000F931F93CF93DF93CDB7DEB767970FB6AD +:103F1000F894DEBF0FBECDBF8C01611105C046E035 +:103F200062E00E94D01A61C1623008F05DC180E099 +:103F300090E07C012FEFE21AF20A9F938F9388EEB4 +:103F400095E09F938F930E94C54664E370E0C8019B +:103F50000E94B61A0F900F900F900F908FEF90E085 +:103F60009E8B8D8BAE014B5E5F4F65E370E0C801A9 +:103F70000E946B1C81110EC085EE95E09F938F937C +:103F80000E94C5469FE7EFE4F2E19150E040F04027 +:103F9000E1F714C060E070E0CB010E94AA0A01962C +:103FA00091F08BED95E09F938F930E94C5468FE72C +:103FB0009FE4E2E181509040E040E1F700C0000062 +:103FC0000F900F9019C08D899E898130910571F4F1 +:103FD00082ED95E09F938F930E94C546FFE72FE403 +:103FE00082E1F15020408040E1F7E8CF8D3091052B +:103FF00019F0419709F0B2CF8D899E898D309105D6 +:1040000021F0419711F0C70194CF81EC95E09F9387 +:104010008F930E94C5460F900F9082E7482E85E04F +:10402000582E96E6692E95E0792E28E8C22E25E0D6 +:10403000D22E30E9A32E35E0B32E41EB842E45E09D +:10404000942EAE014B5E5F4F65E370E0C8010E94A5 +:104050006B1C81110EC0EEEBF5E0FF93EF930E9415 +:10406000C5468FE79FE4E2E181509040E040E1F7F0 +:104070002EC060E070E0CB010E94AA0A019691F088 +:1040800084EB95E09F938F930E94C5462FE78FE4C2 +:1040900092E1215080409040E1F700C000000F9075 +:1040A0000F90A2C0AE01495E5F4F6BE370E0C801A4 +:1040B0000E946B1C81110EC09F928F920E94C54678 +:1040C000EFE7FFE422E1E150F0402040E1F700C0DB +:1040D000000071C08F89843108F46FC024E1AE0103 +:1040E000495E5F4FBE016F5F7F4FC8010E949B1EFC +:1040F000882309F462C08F89882309F45EC084E6AE +:1041000090E00E942B0A8CEA95E09F938F930E9487 +:10411000C546F80185810F900F9082FF24C08481ED +:104120008F771F928F938381282F082E000C330BDB +:104130003F938F9385E995E09F938F930E94C546A7 +:10414000F80184810F900F900F900F900F900F90B7 +:1041500087FF03C0BF92AF9202C0DF92CF920E944E +:10416000C5460F900F908F891F928F935F924F9249 +:104170000E94C5460F900F900F900F90312C8F89A1 +:104180003816A8F4E1E0F0E0EC0FFD1FE30DF11D9F +:1041900080811F928F938CE695E09F938F930E946E +:1041A000C54633940F900F900F900F90E8CF7F92F9 +:1041B0006F920E94C5460F900F908D899E898D3019 +:1041C000910509F43ECF8131910509F43ACF9F93CF +:1041D0008F9382E595E09F938F930E94C5460F9041 +:1041E0000F900F900F900FCF8FEF67960FB6F89448 +:1041F000DEBF0FBECDBFDF91CF911F910F91FF901A +:10420000EF90DF90CF90BF90AF909F908F907F9076 +:104210006F905F904F903F900895FC01268122237C +:1042200089F0278130852130310531F038F0223096 +:10423000310541F40C94731F0C944E1D611102C0A2 +:104240000E94641A8FEF0895AF92BF92CF92DF92CF +:10425000EF92FF920F931F93CF93DF93CDB7DEB70B +:1042600029970FB6F894DEBF0FBECDBF7C0184E75F +:1042700093E09F938F930E94C54688EBC82E86E0FB +:10428000D82E0F900F9010E099E6A92E93E0B92E4A +:1042900041E050E0B601CE0109960E94FE45AE0114 +:1042A0004F5F5F4F612FC7010E94A31B89859981D2 +:1042B000981751F01F928F93BF92AF920E94C546FC +:1042C0000F900F900F900F901F5F8FEFC81AD80AB2 +:1042D0001F32F1F600E310E0AE014F5F5F4FB8010F +:1042E000C7010E946E1C0F5F1F4F0E331105A1F70F +:1042F00028E0AE014F5F5F4F6EE3C7010E94FE1BD7 +:1043000029960FB6F894DEBF0FBECDBFDF91CF91D7 +:104310001F910F91FF90EF90DF90CF90BF90AF90E3 +:1043200008951F93CF93DF93EC010E94811A88E2D6 +:104330008A95F1F7CE010E94831A88EC8A95F1F7ED +:10434000CE010E94811ACE010E948F1A182F60E3BD +:1043500070E0CE010E94B61A8123DF91CF911F91A8 +:104360000895CF92DF92EF92FF921F93CF93DF9346 +:104370001F92CDB7DEB76C0188EBE82E86E0F82EF1 +:1043800010E041E050E0B701CE0101960E94FE45E9 +:104390004981612FC6010E94D01A173039F49981E2 +:1043A00092FF04C0F6019581926095831F5F882378 +:1043B00031F08FEFE81AF80A1F3219F781E00F90F9 +:1043C000DF91CF911F91FF90EF90DF90CF900895F4 +:1043D0005F926F927F928F929F92AF92BF92CF9295 +:1043E000DF92EF92FF920F931F93CF93DF93CDB79E +:1043F000DEB728970FB6F894DEBF0FBECDBF4C01D5 +:104400008E010F5F1F4FE12CF12C60E0702EC12E4A +:1044100086E5A82E83E0B82EDD24D394D60E6E2C2C +:104420005F2C85E0689FB00111246951794F41E00C +:1044300050E0C8010E94FE45EAE0ED1508F4D12CD9 +:10444000F401828584FF10C0F80180811F928F9350 +:104450005F926F92BF92AF920E94C5460F900F90ED +:104460000F900F900F900F90FFEFEF1AFF0A0F5F62 +:104470001F4F88E0E816F10411F06D2DCDCF28E034 +:10448000472D5C2D6EE3C4010E94221B28960FB6B7 +:10449000F894DEBF0FBECDBFDF91CF911F910F917A +:1044A000FF90EF90DF90CF90BF90AF909F908F9054 +:1044B0007F906F905F900895CF93DF93EC012898E1 +:1044C000209A0E94781A3E9A469A8BE291E00197D0 +:1044D000F1F700C00000CE010E947A1A80916400BA +:1044E000847080936400149A0C9A2C9A269884B154 +:1044F000806B84B980E58CBD159A0D988BE095E0B2 +:104500009F938F930E94C546CE010E9491210F90E8 +:104510000F90811103C085E095E01FC08EEE94E0FE +:104520009F938F930E94C546CE010E94B1210F90A8 +:104530000F90811103C088EE94E00FC082ED94E0EB +:104540009F938F930E94C546CE010E94E8210F9051 +:104550000F90811107C08CEC94E09F938F930E9481 +:10456000C5460EC089EC94E09F938F930E94C54688 +:1045700081E08E838FEF8A87CE010E9424211A86E4 +:104580000F900F90DF91CF91089524B1287F24B927 +:1045900025B1276025B921E0FC0122830895FC01A3 +:1045A000128284B1887F84B985B1887F85B90895E6 +:1045B000EF92FF920F931F93CF93DF9361112CC063 +:1045C000EC010CE217E08EE1E82E87E0F82E6AE0BD +:1045D00070E080E090E00E94AA0A0196F9F41F932F +:1045E0000F930E94C5468B811F928F93282F082E10 +:1045F000000C330B3F938F93FF92EF920E94C546BE +:104600008DB79EB708960FB6F8949EBF0FBE8DBFAC +:104610001A99DDCF1B82DBCF8FEF01C080E0DF91E5 +:10462000CF911F910F91FF90EF90089583B182FB7E +:10463000882780F991E08927089583B1809581705A +:10464000089583B18695817091E089270895FC01D2 +:104650008281811102C01382089533B191E039271C +:10466000379533273795330F330B23B126952170B8 +:104670002927279522272795220F220B80E030FB40 +:1046800080F920FB81F990911F0198279370D1F454 +:1046900090911E0190FD0EC0982791FF0BC0938151 +:1046A00081FD04C09F3729F09F5F03C0903809F057 +:1046B0009150938380911E0130FB80F920FB81F99A +:1046C00080931E0180911F0130FB80F920FB81F94E +:1046D00080931F010895FC018281938183309105AD +:1046E00040F4880F991FFC01E659FE4F80819181AB +:1046F00008958AE991E00895E8EBF0E02DE0208349 +:104700002CE1208324E6208324E02093BC0020E6D3 +:1047100020937C0027E820937A0080579F4F21E068 +:10472000FC012083089580579F4FFC01108284E094 +:104730008093BC001092B80010927C0010927A0016 +:1047400008952F923F924F925F926F927F928F9235 +:104750009F92AF92BF92CF92DF92EF92FF920F9310 +:104760001F93CF93DF93CDB7DEB761970FB6F89461 +:10477000DEBF0FBECDBF8C016111A5C2FC018281DD +:104780009381009709F0E8C182EC98E09F938F93A2 +:104790000E94C54698012C5F3F4F3D872C8767E7F5 +:1047A000C9010E94350F0F900F90811103C08FEB4C +:1047B00098E08BC28FEA98E09F938F930E94C54642 +:1047C00058013DE3A30EB11CC5010E9456170F907E +:1047D0000F90811103C08CEA98E077C262E0C501B6 +:1047E0000E944317811103C089EA98E06EC200E07D +:1047F00010E022E832E440E050E068EC71E4C501EA +:104800000E94061A811103C086EA98E05EC283EA1C +:1048100098E09F938F930E94C5460F900F908FEF63 +:104820008F83412C512C32014E865F86688A798AAB +:104830008C859D850E94F80F20E030E543EC57E41D +:104840000E9481419F938F937F936F93ECE8F8E0F0 +:10485000FF93EF930E94C5468C859D850E94470F6C +:1048600068877987382E292E9F938F9339853F93B8 +:1048700088858F93EFE7F8E0FF93EF930E94C5469A +:104880008C859D850E94DE124B018A879B879F93B2 +:104890008F939F926F9383E798E09F938F930E94EB +:1048A000C5460FB6F894DEBF0FBECDBF3F813F3F78 +:1048B00009F19A858B85282D392D492F582FFC0118 +:1048C000682D792D8F2F9E2F0E94C843811112C011 +:1048D00078856985C101272F362F492F582FDB0195 +:1048E000F1016B2F7A2F8F2F9E2F0E94C8438823B0 +:1048F00009F4FBC165E0C62ED12CE12CF12C05E0BA +:1049000010E025E030E045E050E063E070E08C85A9 +:104910009D850E940B0D8F81803208F413C1E6E65D +:10492000F8E0FF93EF930E94C5460F900F903F81F0 +:1049300013162CF5632F330F770B880B990B0E94FE +:1049400031426B017C019B01AC01C301B2010E94A9 +:1049500081414B018A879B87A70196016E857F85E0 +:10496000888999890E94814168877987382E292E04 +:10497000412C512C32014E865F86688A798A9A854D +:104980008B85082D192D292F382FF885E985C10130 +:104990004F2F5E2F692F722DC5010E94061A8111BB +:1049A00005C082E698E09F938F93C7C08B858F9355 +:1049B0009A859F939F928F922F923F92E985EF93D2 +:1049C000F885FF9320E538E03F932F930E94C5467A +:1049D00062E0C5010E9443170FB6F894DEBF0FBE18 +:1049E000CDBF811103C08DE498E093C014E687EC3D +:1049F0009FEA0197F1F700C00000BE016A5F7F4F98 +:104A0000C5010E945F18882309F48AC08E8181FF46 +:104A100087C0BE016F5F7F4FC5010E946318882366 +:104A200009F481C089811F928F93E4E4F8E0FF9339 +:104A3000EF930E94C5460F900F900F900F908981C1 +:104A4000833091F038F4813061F0823099F485E35D +:104A500098E012C0843059F0853061F48DE198E01F +:104A60000BC08AE398E008C08CE298E005C087E2BA +:104A700098E002C08BE198E09F938F930E94C54617 +:104A80000F900F908B818F938A818F938DE098E0A8 +:104A90009F938F930E94C5468D818F938C818F93B6 +:104AA00022E038E03F932F930E94C5468C819D8180 +:104AB0000FB6F894DEBF0FBECDBF8039F1E09F077F +:104AC00018F480E098E01DC0883522E0920718F4C1 +:104AD00086EF97E016C0803233E0930718F481EF39 +:104AE00097E00FC0883EE3E09E0718F48CEE97E055 +:104AF00008C08C3D954018F487EE97E002C083EE25 +:104B000097E09F938F930E94C5460F900F9081EE80 +:104B100097E09F938F930E94C5460F900F9003C01C +:104B2000115009F064CF61E0C5010E944317811163 +:104B300008C02EED37E03F932F930E94C5460F909B +:104B40000F901F8268EE73E080E090E00E94AA0A56 +:104B5000019609F46DCEC1C08130910509F071C094 +:104B600007581F4F61E0C8010E94F107811103C07F +:104B70008BED97E0AAC09CEB692E97E0792E22EB93 +:104B8000E22E27E0F22E32EAC32E37E0D32E41E99F +:104B9000A42E47E0B42E5BE9852E57E0952E809138 +:104BA0007A00806480937A0080917A0086FDFCCF41 +:104BB0008091790089831F928F937F926F920E94D8 +:104BC000C54641E0BE016F5F7F4FC8010E94C90822 +:104BD0000F900F900F900F90811106C0FF92EF92EF +:104BE0000E94C5460F900F90DF92CF920E94C5465B +:104BF00041E0BE016F5F7F4FC8010E94A6080F9081 +:104C00000F90882361F089811F928F939F928F92DA +:104C10000E94C5460F900F900F900F9006C0BF9254 +:104C2000AF920E94C5460F900F9068EE73E080E04F +:104C300090E00E94AA0A019609F4B1CF0E94FB07F6 +:104C40004CC0029709F03FC005581F4F40E061E09B +:104C5000C8010E945A09811103C08EE897E035C04F +:104C600088E5C82E87E0D82EC8010E94AA097C01D9 +:104C70000196F1F080917A00806480937A008091AF +:104C80007A0086FDFCCF60917900C8010E948A09F4 +:104C9000809179001F928F93FF92EF92DF92CF92D3 +:104CA0000E94C5460F900F900F900F900F900F909D +:104CB00060E070E0CB010E94AA0A0196A9F2C80147 +:104CC0000E946A090AC084E597E09F938F930E942F +:104CD000C5460F900F908FEF29C064EF71E080E020 +:104CE00090E00E94AA0A80E021C078856985C10110 +:104CF000272F362F492F582F6E857F8588899989D0 +:104D00000E9415416E877F87888B998B9A858B854A +:104D1000282D392D492F582FC301B2010E9415416A +:104D20002B013C018F818F5F8F83E4CD61960FB69D +:104D3000F894DEBF0FBECDBFDF91CF911F910F91D1 +:104D4000FF90EF90DF90CF90BF90AF909F908F90AB +:104D50007F906F905F904F903F902F900895FC014F +:104D6000228133812230310541F4579A5F9A855868 +:104D70009F4F0E94AF095F9808958091BC008068A2 +:104D80008093BC00089520E620937C0027E82093C0 +:104D90007A00289A299824B1236024B95A9A22E0EB +:104DA0002093C80028E92093C90026E02093CA0078 +:104DB0001092CD002BE92093CC0021E0FC0122834E +:104DC0000895FC01128210927C0010927A001092D9 +:104DD000C8001092C9001092CA001092CD00109223 +:104DE000CC005A9884B18C7F84B985B18C7F85B909 +:104DF0000895CF92DF92EF92FF920F931F93CF937C +:104E0000DF938C0161114EC02898299889E1E82E22 +:104E100089E0F82ECC24CA94DC2CCEEFD8E064EFE5 +:104E200071E080E090E00E94AA0A019609F044C077 +:104E300080917A00806480937A0080917A0086FD68 +:104E4000FCCF8091C80080648093C800289A299A7A +:104E5000809179008093CE008091C80086FFFCCFBE +:104E600029982898809179001F928F93FF92EF9252 +:104E70000E94C546F894F80183819481D482C3824C +:104E800078940F900F900F900F9097FDC8CF9F933D +:104E90008F93DF93CF930E94C5460F900F900F9092 +:104EA0000F90BDCF8AEF98E09F938F930E94C546E5 +:104EB0000F900F908FEF01C080E0DF91CF911F9195 +:104EC0000F91FF90EF90DF90CF90089570E0FC017C +:104ED0007483638308955F9A08955F9808955E9A36 +:104EE00008955E980895139A0895139808958FEF82 +:104EF00084B9089580918804813019F48FEF84B9C2 +:104F0000089514B8089580918804813041F487EBA6 +:104F10009BE00197F1F700C0000080E0089583B1A5 +:104F2000089565B90895109A0895109808951F93EB +:104F3000CF93DF93EC01162F0E9475278FEF84B972 +:104F40008A81882389F0812F807F85B9CE010E94D4 +:104F500073278CE38A95F1F7CE010E94752784E0D0 +:104F60008A95F1F71295107F15B9CE010E9473272B +:104F70008CE38A95F1F7CE010E94752784E08A952B +:104F8000F1F7DF91CF911F9108951F93CF93DF9396 +:104F9000EC01162F0E947127CE010E946D27612F10 +:104FA000CE01DF91CF911F910C949727EF92FF9242 +:104FB0000F931F93CF93DF93EC017B018B8187FDD0 +:104FC00059C080918804813041F487EB9BE00197C0 +:104FD000F1F700C0000081E04EC0CE010E947A27A8 +:104FE0008FEF85B9CE010E946F27CE010E946D27F9 +:104FF00094E09A95F1F7CE010E9473278CE38A958D +:10500000F1F7CE010E948327082FCE010E94752759 +:1050100094E09A95F1F7CE010E9473278CE38A956C +:10502000F1F7CE010E94832782958F70007F182FA1 +:10503000102BCE010E94752794E09A95F1F7CE01CE +:105040000E947127912F90788BE0E816F10430F0E0 +:105050008BE0E81AF10811F09111C4CF8B818130F7 +:1050600021F4992311F08BEF8B8315B88FEF84B95E +:1050700081E0911180E0DF91CF911F910F91FF901E +:10508000EF900895CF93DF93EC0162E370E00E940C +:10509000D627811102C08AEF8B83DF91CF910895CB +:1050A0001F93CF93DF93EC01162F0E944228612FAC +:1050B0006068CE010E94C527CE01DF91CF911F917C +:1050C0000C9442281F93CF93DF93EC0115B88FEF18 +:1050D00084B98FE79BEB0197F1F700C000001B82BA +:1050E00010E08A81882311F068E201C068E0CE01F7 +:1050F0000E94C527111105C087E99AE30197F1F7CE +:1051000004C08BE291E00197F1F700C000001F5F3F +:10511000143039F76CE0CE010E94C52762E370E0DD +:10512000CE010E94D627811102C08FEF1BC06CE018 +:10513000CE010E94C52762E370E0CE010E94D6270F +:10514000811102C08DEF0EC061E0CE010E94C52723 +:1051500060EB74E0CE010E94D627811102C08CEF73 +:1051600001C081E08B83DF91CF911F910895CF9390 +:10517000DF93EC0181B1896081B98FEF84B98AB185 +:10518000806C8AB9CE010E9462288CE599E09F93D9 +:105190008F930E94C5468B810F900F90813049F408 +:1051A00088E599E09F938F930E94C5460F900F90DA +:1051B00010C0282F082E000C330B3F938F938EE4E2 +:1051C00099E09F938F930E94C5460F900F900F9088 +:1051D0000F90CE01DF91CF910C949327CF93DF9363 +:1051E000FC012381213061F4EC010E9442286CE033 +:1051F000CE010E94C527CE01DF91CF910C944228A9 +:10520000DF91CF910895CF93DF93FC01238121306B +:1052100061F4EC010E94422868E0CE010E94C5279B +:10522000CE01DF91CF910C944228DF91CF91089568 +:10523000CF93DF93FC012381213061F4EC010E94C4 +:10524000422861E0CE010E94C527CE01DF91CF91B7 +:105250000C944228DF91CF9108950E94182982B1C1 +:10526000867F82B981B1867F81B915B814B88BB1B8 +:105270008F738BB98AB18F738AB90895CF93DF93F7 +:10528000EC018B818130B9F44431A8F4613039F0FC +:1052900048F0623031F0633071F44C5A03C0405C26 +:1052A00001C04C5E642FCE010E945028CE01DF91D8 +:1052B000CF910C944228DF91CF9108951F93CF9303 +:1052C000DF93FC0123812130A1F4162FEC010E9411 +:1052D0007127CE010E946B27612FCE010E94972774 +:1052E000CE010E946D27CE01DF91CF911F910C94CA +:1052F0004228DF91CF911F910895FF920F931F9342 +:10530000CF93DF938C0161114FC0C0E084E1F82E90 +:10531000D0E2DC0F8C2F6F2D0E94E93E911105C069 +:1053200040E0682FC8010E943E296D2FC8010E94ED +:105330005E29C8010E944228CF5FC03549F789E441 +:1053400099E09F938F930E94C546F80183810F9047 +:105350000F90813049F486E499E09F938F930E94E7 +:10536000C5460F900F9010C0282F082E000C330B4D +:105370003F938F938CE399E09F938F930E94C54650 +:105380000F900F900F900F9061E070E080E090E040 +:105390000E94AA0A0196C1F364EF71E080E090E0F8 +:1053A0000E94AA0A80E009C088E399E09F938F9346 +:1053B0000E94C5460F900F908FEFDF91CF911F9104 +:1053C0000F91FF9008950F931F93CF93DF93EC01FC +:1053D0008B01F80161918F01662339F08B81813057 +:1053E00021F4CE010E945E29F4CFDF91CF911F916D +:1053F0000F9108958BB18F708BB98AB1806F8AB984 +:1054000008958AB18F708AB98BB18F708BB9089566 +:105410008130910549F030F08230910539F00397E1 +:1054200039F008955C9A08955D9A08955E9A0895FA +:105430005F9A0895CB0141110C94082A61307105DF +:1054400051F038F06230710541F06330710539F088 +:1054500008955C9808955D9808955E9808955F9802 +:1054600008956130710559F038F06230710551F0DE +:105470006330710559F008959BB180E105C09BB17F +:1054800080E202C09BB180E4892702C08BB18058C2 +:105490008BB90895CB010C94082A40E00C941A2A89 +:1054A000CF936031E8F5C62FC370C23091F0C3309E +:1054B000B9F0C13039F063E070E00E944D2A80E01D +:1054C00090E014C060E070E00E944D2A81E090E01E +:1054D0000DC061E070E00E944D2A82E090E006C0BD +:1054E00062E070E00E944D2A83E090E00E94082A6A +:1054F0006C2F70E06F5F7F4F7F936F938BE699E027 +:105500009F938F930E94C54664EF71E080E090E026 +:105510000E94AA0A0F900F900F900F9080E001C098 +:105520008FEFCF9108950895FC0112821092C80068 +:105530001092C9001092CA001092CD001092CC00B7 +:105540005A9884B18C7F84B985B18C7F85B90895D0 +:105550002F923F924F925F926F927F928F929F9283 +:10556000AF92BF92CF92DF92EF92FF920F931F9371 +:10557000CF93DF9300D000D0CDB7DEB79C838B8371 +:10558000611121C0289A299884B1836084B95A9AFC +:1055900082E08093C80088E98093C90086E0809308 +:1055A000CA001092CD008BE98093CC0081E0EB81A2 +:1055B000FC81828384E59AE09F938F930E94C54685 +:1055C0000F900F903DC18FEF860F843008F06EC0B2 +:1055D000633039F0643041F0623051F41EE801E08C +:1055E00009C015E500E002C01AEA01E0F12C04C090 +:1055F00011E000E0FF24F3941F921F931F92FF928B +:105600001F920F9385E39AE09F938F930E94C54664 +:105610000FB6F894DEBF0FBECDBFFF2011F0289A61 +:1056200001C02898002311F0299A01C02998EBE2C3 +:10563000F1E03197F1F700C00000EB81FC811382AB +:105640001093CE0087EB9BE00197F1F700C00000BC +:105650008381882389F084811F928F9385E29AE069 +:105660009F938F930E94C546EB81FC8113820F901C +:105670000F900F900F9008C084E19AE09F938F9352 +:105680000E94C5460F900F908AEF99E09F938F93E9 +:105690000E94C5460F900F906FEF7FEFCB010E94E5 +:1056A000AA0A0196C9F32998289ACAC0653009F058 +:1056B000BDC08DEB99E09F938F930E94C546299AB8 +:1056C00028988BE291E00197F1F700C000000F905D +:1056D0000F908FEA282E89E0382E97EA492E99E01C +:1056E000592E29E9622E29E0722E37E9832E39E0FE +:1056F000932E44E9E42E49E0F42E5CE8C52E59E0EF +:10570000D52E60E8A62E69E0B62E299AEB81FC81A1 +:10571000138200E711E0F8018091C8008064809353 +:10572000C80081918093CE008091C80085FFFCCF96 +:1057300021E0E837F20781F78091C80086FFFCCFAF +:1057400029983F922F920E94C5460F900F90F80122 +:1057500081918F011F928F935F924F920E94C54655 +:105760000F900F900F900F90F1E008371F0779F717 +:1057700064E670E080E090E00E94AA0A9A83898340 +:105780007F926F920E94C5460F900F90EB81FC8133 +:105790008381882311F010E01FC09F928F920E9496 +:1057A000C5460F900F901DC0183031F4FF92EF9254 +:1057B0000E94C5460F900F90EB81FC81E10FF11D17 +:1057C00084811F928F93DF92CF920E94C5461F5F04 +:1057D0000F900F900F900F90EB81FC818381181731 +:1057E00018F3EB81FC818381803190F0828991896B +:1057F000208937853F932F939F938F93BF92AF92CA +:105800000E94C5460F900F900F900F900F900F9031 +:1058100089819A810196A1F468EE73E080E090E0BE +:105820000E94AA0A019609F470CF0AC08CE799E099 +:105830009F938F930E94C5460F900F908FEF07C0E4 +:1058400064EF71E080E090E00E94AA0A80E00F908F +:105850000F900F900F90DF91CF911F910F91FF90BC +:10586000EF90DF90CF90BF90AF909F908F907F9000 +:105870006F905F904F903F902F900895FC0123818F +:10588000203130F431E0320F3383E20FF11D6483B5 +:105890000895579810927C0010927A0014BC15BCA1 +:1058A00024B1277E24B91398FC011282089580B197 +:1058B00083FB882780F991E0892708952C98089523 +:1058C0002C9A0895CF93DF93EC01579A80E680934A +:1058D0007C0087E880937A0083E884BD84E085BDFE +:1058E00084B1886184B9139ACE010E94602C81E052 +:1058F0008A83DF91CF9108952F923F924F925F92CA +:105900006F927F928F929F92AF92BF92CF92DF92CF +:10591000EF92FF920F931F93CF93DF936111E1C03A +:10592000EC0185EE9AE09F938F930E94C5460F90FD +:105930000F9085ED482E8AE0582E91EC692E9AE062 +:10594000792E02EB1AE028EAE22E2AE0F22E3DE957 +:10595000C32E3AE0D32E41E9A42E4AE0B42E50E6FD +:10596000252E5AE0352E69E7862E6AE0962E6AE0EB +:1059700070E080E090E00E94AA0A019609F0B3C0AE +:1059800080B183FB882780F91F928F938AED9AE07C +:105990009F938F930E94C5460F900F900F900F908A +:1059A000CE01039905C00E945E2C5F924F9206C003 +:1059B0000E94602C80ED9AE09F938F930E94C546D1 +:1059C0000F900F9080917A00806480937A0080918C +:1059D0007A0086FDFCCF809179001F928F937F9291 +:1059E0006F920E94C54682EE80937C008091790080 +:1059F00090E005970F900F900F900F9097FF02C0C7 +:105A000080E090E0809587BD1F928F931F930F9346 +:105A10000E94C54680917A00806480937A000F903E +:105A20000F900F900F9080917A0086FDFCCF8091AF +:105A300079001F928F93FF92EF920E94C54690E6E5 +:105A400090937C0083B182FB882780F91F928F930B +:105A5000DF92CF920E94C54683B181701F928F93CF +:105A6000BF92AF920E94C546F8946B817C81789476 +:105A70008DB79EB70C960FB6F8949EBF0FBE8DBF24 +:105A80006115710519F17F936F9380E090E00E949A +:105A90002F429B01AC0160E070E080E792E40E943D +:105AA000814127E137EB41ED58E30E9481410E949B +:105AB000F3417F936F939F928F920E94C5460F9000 +:105AC0000F900F900F900F900F9051CF1F921F9239 +:105AD0003F922F920E94C5460F900F900F900F900B +:105AE00046CF8FEF01C080E0DF91CF911F910F91E2 +:105AF000FF90EF90DF90CF90BF90AF909F908F90EE +:105B00007F906F905F904F903F902F90089583B15A +:105B100082FB882780F991E08927089583B18170FD +:105B200008958BB180588BB908951F93CF93DF935D +:105B3000EC0123B18091DD049091DE04122F1170ED +:105B400020FD1BC02091E104222309F12091DF04F4 +:105B50003091E00423303105D0F0820F931F9C83F5 +:105B60008B831092E0041092DF041092DE041092F6 +:105B7000DD04CE010E94912D0AC0811520E4920718 +:105B800048F501969093DE048093DD040DC080916A +:105B9000DF049091E004811520E49207F0F401966F +:105BA0009093E0048093DF048091DD049091DE0403 +:105BB0008115904438F48091DF049091E0048115C0 +:105BC000904410F01C821B821093E104DF91CF916E +:105BD0001F91089580E090E4D5CF80E090E4E0CF7D +:105BE000E4E6F0E0808184708083179A0F9A2C9A03 +:105BF00084B1806B84B926988CB583658CBD08957B +:105C0000269884B18F7584B90F9817981CBC089595 +:105C100017980895179A08950F931F93CF93DF93C2 +:105C2000EC01062F142F0E94082E80E48EBD0DB4C7 +:105C300007FEFDCF0EBD0DB407FEFDCF1EBD0DB49A +:105C400007FEFDCFCE010E940A2E84E18A95F1F76E +:105C500080E0DF91CF911F910F9108951F93CF9313 +:105C6000DF93EC01162F0E94082E81E48EBD0DB447 +:105C700007FEFDCF1EBD0DB407FEFDCF1EBC0DB44B +:105C800007FEFDCFCE010E940A2E8EB5DF91CF9187 +:105C90001F910895CF93DF93C42FD22F1F924F935C +:105CA0001F926F9388E79BE09F938F930E94C54656 +:105CB0000F900F900F900F900F900F90CD1769F0ED +:105CC00081E79BE09F938F930E94C5460F900F90B2 +:105CD000CF3F59F486E59BE002C082E59BE09F93AD +:105CE0008F930E94C5460F900F9080E59BE09F9395 +:105CF0008F930E94C5460F900F90DF91CF9108952A +:105D00002F923F924F925F926F927F928F929F92CB +:105D1000AF92BF92CF92DF92EF92FF920F931F93B9 +:105D2000CF93DF936111D8C0EC0181E4282E8BE082 +:105D3000382ECC24C394D12C2CE2422E2BE0522EB0 +:105D400037E1632E3BE0732E42E0A42E4BE0B42EED +:105D50005DEE852E5AE0952E64EF71E080E090E0D4 +:105D60000E94AA0A019609F0B9C03F922F920E94A0 +:105D7000C5460F900F90E12CF12C86010E2C01C02E +:105D8000000F0A94EAF7402F409560E0CE010E9490 +:105D90000C2E402F62E1CE010E940C2EFF92EF925A +:105DA0005F924F920E94C54662E1CE010E942E2E64 +:105DB000202F482F62E1CE010E944A2E68EC70E04D +:105DC00080E090E00E94AA0A40E062E1CE010E94D9 +:105DD0000C2EFF92EF927F926F920E94C54662E175 +:105DE000CE010E942E2E20E0482F62E1CE010E94BB +:105DF0004A2E4FEF60E0CE010E940C2E68EC70E05E +:105E000080E090E00E94AA0A8FEFE81AF80A8DB7A6 +:105E10009EB708960FB6F8949EBF0FBE8DBF98E050 +:105E2000E916F10409F0A9CF00E010E07601002E98 +:105E300001C0EE0C0A94EAF74E2D409561E0CE01C8 +:105E40000E940C2E4E2D63E1CE010E940C2E1F935A +:105E50000F93BF92AF920E94C54663E1CE010E94AC +:105E60002E2E2E2D482F63E1CE010E944A2E68EC83 +:105E700070E080E090E00E94AA0A40E063E1CE0179 +:105E80000E940C2E1F930F939F928F920E94C546E3 +:105E900063E1CE010E942E2E20E0482F63E1CE0167 +:105EA0000E944A2E4FEF61E0CE010E940C2E68EC5A +:105EB00070E080E090E00E94AA0A0F5F1F4F8DB74C +:105EC0009EB708960FB6F8949EBF0FBE8DBF0830E0 +:105ED000110509F0ABCF40CF8FEF01C080E0DF911B +:105EE000CF911F910F91FF90EF90DF90CF90BF90D7 +:105EF000AF909F908F907F906F905F904F903F906A +:105F00002F90089580E480937C0087E880937A0046 +:105F1000089510927C0010927A0008950F931F93B9 +:105F2000CF93DF9361113AC08AEB9BE09F938F93ED +:105F30000E94C5460F900F9005EA1BE0C8E9DBE020 +:105F40006AE070E080E090E00E94AA0A019641F5C4 +:105F50001F930F930E94C54680917A0080648093BE +:105F60007A000F900F9080917A0086FDFCCF2091EF +:105F700078003091790080917800909179003F937A +:105F80002F939F938F93DF93CF930E94C5460F90DB +:105F90000F900F900F900F900F90D2CF8FEF01C006 +:105FA00080E0DF91CF911F910F91089582E480935B +:105FB0007C0087E880937A00089510927C0010920C +:105FC0007A000895AF92BF92CF92DF92EF92FF9244 +:105FD0000F931F93CF93DF9361117FC083E09CE009 +:105FE0009F938F930E94C5460F900F903EEEE32E35 +:105FF0003BE0F32E01EE1BE0C1ECDBE06AE070E079 +:1060000080E090E00E94AA0A019609F068C0FF9221 +:10601000EF920E94C54680917A00806480937A0056 +:106020000F900F9080917A0086FDFCCF2091780030 +:106030003091790080917800909179003F932F936F +:106040009F938F931F930F930E94C5466091780092 +:106050007091790080E090E00E942F422AE939E9AE +:1060600041E852E40E9481412DEC3CEC4CE05FE3BE +:106070000E941541D62EC72EB82EA92EA601950135 +:10608000652F742F832F922F0E94FA41862F90E064 +:1060900061701F926F9381FB222720F91F922F932B +:1060A00082FB222720F91F922F9323E09595879555 +:1060B0002A95E1F79F938F93AF92BF92CF92DF9291 +:1060C000DF93CF930E94C5468DB79EB744960FB617 +:1060D000F8949EBF0FBE8DBF91CF8FEF01C080E0BF +:1060E000DF91CF911F910F91FF90EF90DF90CF90B4 +:1060F000BF90AF900895442371F06130710539F07D +:1061000020F06230710529F00895289808952998A3 +:1061100008952A9808956130710539F020F06230B1 +:10612000710529F00895289A0895299A08952A9AC0 +:1061300008956130710541F020F06230710539F049 +:10614000089595B181E005C095B182E002C095B196 +:1061500084E0892785B9089541E00C947B3040E0C4 +:106160000C947B30CF93DF93EC0160E070E00E94F1 +:10617000AF3061E070E0CE010E94AF3062E070E0CD +:10618000CE010E94AF3084B1876084B9DF91CF9196 +:106190000895CF93DF93EC0160E070E00E94AF3090 +:1061A00061E070E0CE010E94AF3062E070E0CE01AD +:1061B0000E94AF3084B1887F84B9DF91CF91089578 +:1061C000CF93DF93EC01613009F43FC058F16230A6 +:1061D00009F44EC0633009F064C060E070E00E94D2 +:1061E000AC3061E070E0CE010E94AC3062E070E063 +:1061F000CE010E94AC3089E09CE09F938F930E9477 +:10620000C54668EB7BE080E090E00E94AA0A60E06F +:1062100070E0CE010E94AF3061E070E0CE010E94DC +:10622000AF3036C060E070E00E94AC3088E19CE0A6 +:106230009F938F930E94C54668EB7BE080E090E0DF +:106240000E94AA0A60E070E025C061E070E00E9450 +:10625000AC3082E19CE09F938F930E94C54668EB2F +:106260007BE080E090E00E94AA0A61E070E012C04A +:1062700062E070E00E94AC308DE09CE09F938F93D1 +:106280000E94C54668EB7BE080E090E00E94AA0A8D +:1062900062E070E0CE010E94AF300F900F9080E07E +:1062A00001C08FEFDF91CF91089547983F9AE8EBB7 +:1062B000F0E02DE020832CE1208324E6208324E0FD +:1062C0002093BC0021E0FC0124830895FC0114828A +:1062D00084E08093BC001092B800089587EB9EE0A4 +:1062E0000895ECEBF0E08081806880830895862F2C +:1062F0008695869586958E71982F990F990F890F9F +:106300006F70860F08950F931F93CF93DF938C01C7 +:10631000EA019E859C7F92609E8781E0611101C0A9 +:1063200080E09E8580FB97F99E876F87998180FB2F +:1063300090F99B7F98609983662389F01F929F93C1 +:106340001F926F938BEF9CE09F938F930E94C546A3 +:106350000F900F900F900F900F900F9008C08AEE43 +:106360009CE09F938F930E94C5460F900F900E5F05 +:106370001F4F21E0AE014F5F5F4F61E0C8010E94F7 +:1063800074088823A1F0AE01425F5F4F22E06EE007 +:10639000C8010E947408C82F882349F087EE9CE04A +:1063A0009F938F930E94C5460F900F9009C081EE76 +:1063B0009CE09F938F930E94C5460F900F90C0E082 +:1063C0008C2FDF91CF911F910F910895CF92DF9283 +:1063D000EF92FF920F931F93CF93DF937C01EA011B +:1063E000479B10C082ED9CE09F938F930E94C5460F +:1063F0003F9A4798AE0160E0C7010E9483310F9039 +:106400000F905CC080ED9CE09F938F930E94C546E7 +:106410000F900F9009E010E08EE8C82E8CE0D82E87 +:106420001F930F93DF92CF920E94C54668EE73E0F0 +:1064300080E090E00E94C50A0F900F900F900F909F +:106440000130110531F0015011098F3F2FEF9207F4 +:1064500039F34B9799F1AE016AE0C7010E9483318D +:10646000898188608B7F81608983AE014F5F5F4F38 +:1064700021E061E0C70102960E9474088AE79CE06F +:106480009F938F930E94C5463F9A479A87E99AE364 +:106490000197F1F700C000003F98479868E873E162 +:1064A00080E090E00E94C50A88E49CE09F938F936F +:1064B0000E94C5460F900F900F900F9081E0DF91E2 +:1064C000CF911F910F91FF90EF90DF90CF900895A3 +:1064D0008F929F92AF92BF92CF92DF92EF92FF92F4 +:1064E0000F931F93CF93DF93CDB7DEB727970FB6E8 +:1064F000F894DEBF0FBECDBF4C01362FCA011E2D52 +:10650000FC2DEA2DBA01605D774051E065367105DA +:1065100008F450E04E8150FB47F94E8364E670E08A +:106520000E94033F6AE00E94E93E8295807F982B9B +:106530009F8337FF02C0395FFCCF373014F03750EC +:10654000FCCF37708D81887F382B3D83121614F075 +:10655000245FFCCF2D3014F02C50FCCF822F6AE04A +:106560000E94F53E9F702E81207F922B80FB94F934 +:106570009E83101614F0015EFCCF003214F00F5110 +:10658000FCCF802F6AE00E94F53E9F703C81307CFA +:106590008370282F2295207F832F892B822B8C8339 +:1065A00017FF02C0185EFCCF183114F01851FCCF51 +:1065B000812F6AE00E94F53E9F703B81307C8370A2 +:1065C000282F2295207F832F892B822B8B83F7FF07 +:1065D00002C0F45CFCCFFC3314F0FC53FCCF8F2FD3 +:1065E0006AE00E94F53E9F703A8130788770282FCC +:1065F0002295207F832F892B822B8A83E7FF02C07D +:10660000E45CFCCFEC3314F0EC53FCCF8E2F6AE04B +:106610000E94F53E9F70398130788770282F22952F +:10662000207F832F892B822B8983CE0107969F930E +:106630008F9301979F938F9301979F938F930197C8 +:106640009F938F9301979F938F9301979F938F931E +:106650008E010F5F1F4F1F930F9386E29CE09F9365 +:106660008F930E94C54678015E0188E0A80EB11C98 +:106670000FB6F894DEBF0FBECDBF80E2C82E8CE00F +:10668000D82EF70181917F011F928F93DF92CF92D5 +:106690000E94C5460F900F900F900F90AE14BF044C +:1066A00081F727E0A80162E0C40102960E94740805 +:1066B00027960FB6F894DEBF0FBECDBFDF91CF9106 +:1066C0001F910F91FF90EF90DF90CF90BF90AF9010 +:1066D0009F908F9008952F923F924F925F926F926A +:1066E0007F928F929F92AF92BF92CF92DF92EF9262 +:1066F000FF920F931F93CF93DF93CDB7DEB76E97C3 +:106700000FB6F894DEBF0FBECDBF611166C22C017B +:106710009C012E5F3F4F3D8F2C8F61E5C9010E9488 +:10672000F107CE0101969A8F898F80E1E98DFA8D6C +:1067300011928A95E9F781EA9EE09F938F930E94D8 +:10674000C5468A8181608A8342E0BE016F5F7F4FC8 +:106750008C8D9D8D0E94C9080F900F90811103C0F0 +:1067600084E093E002C08FEF92E09F938F930E94AA +:10677000C5460F900F9088E99EE09F938F930E94EB +:10678000C54680E89EE09F938F930E94C5468EE5A4 +:106790009EE09F938F930E94C54688E49EE09F935E +:1067A0008F930E94C5468EE29EE09F938F930E9436 +:1067B000C54688E19EE09F938F930E94C54681E085 +:1067C0009EE09F938F930E94C5468CEE9DE09F9321 +:1067D0008F930E94C54686ED9DE09F938F930E9404 +:1067E000C5468EEB9DE09F938F930E94C54685EA38 +:1067F0009DE09F938F930E94C5460FB6F894DEBF2D +:106800000FBECDBF188E86E89DE09F938F930E94A8 +:10681000C54600E19E012F5F3F4F41E0BE01685E2B +:106820007F4F8C8D9D8D0E9411090F900F908111CB +:1068300010C084E093E09F938F930E94C54668EE5A +:1068400073E080E090E00E94C50A4C010F900F9029 +:10685000BDC127E030E047E050E0BE016D5F7F4FF3 +:10686000CE0141960E9412469E012F5F3F4F790153 +:1068700010E0812F837041F484E89DE09F938F9313 +:106880000E94C5460F900F90F70181917F011F92E2 +:106890008F932EE73DE03F932F930E94C5461F5FE5 +:1068A0000F900F900F900F90103119F78E8987FD80 +:1068B00003C040ED57E002C044E358E08F89982FB1 +:1068C0009695969596959E71292F220F220F920FDD +:1068D0008F70890F1A01280E311C8E89982F96957A +:1068E000969596959E71792E770C770C790E8F7010 +:1068F000780E672C8C89982F9695969596959E7113 +:10690000B92EBB0CBB0CB90E8F70B80EBB8E8B8929 +:10691000982F9695969596959E71D92EDD0CDD0C47 +:10692000D90E8F70D80EED2C8A89982F9695969552 +:1069300096959E71F92EFF0CFF0CF90E8F70F80ED4 +:10694000CF2C8989182F1695169516951E71912FA3 +:10695000990F990F190F8F70180FA12E0D890E8F97 +:10696000073048F4E3E00E02C00111249C012054DA +:10697000314F490104C03BE7832E3DE0932E84E76D +:106980009DE09F938F930E94C5469F928F920E9495 +:10699000C546812F012E000C990B9F931F938F2DBD +:1069A0000F2C000C990B9F93FF928D2D0D2C000C3A +:1069B000990B9F93DF928B2D0B2C000C990B9F93BF +:1069C000BF92872D072C000C990B9F937F923F92CB +:1069D0002F92802F002E000C990B9F930F9321E58F +:1069E0003DE03F932F930E94C54688891F928F9365 +:1069F000E1E4FDE0FF93EF930E94C54668EE73E08B +:106A000080E090E00E94C50A4C010FB6F894DEBF0A +:106A10000FBECDBF94E68916910409F490C0F4F539 +:106A2000FEE48F16910409F494C08CF438E48316C4 +:106A3000910409F487C08DE48816910409F47BC0A1 +:106A400094E48916910409F0C1C0BA9479C0E7E5CD +:106A50008E16910409F485C044F423E5821691044E +:106A600009F0B4C0AA24AA9478C039E58316910429 +:106A700009F45AC083E68816910409F0A7C08AE099 +:106A8000A82E9CE0C92E21E1E22E00E128E048EE8C +:106A900057E065E0C2010E94683298C090E789160D +:106AA0009104D1F194F4FDE68F16910409F440C0ED +:106AB0002EE68216910409F449C038E68316910443 +:106AC00009F084C0EE24E3943FC084E78816910463 +:106AD000B1F044F4E3E78E16910409F077C0AA24DC +:106AE000A3943BC0F7E78F169104C9F129E78216FA +:106AF000910409F06BC03FEF231A330A34C08F852D +:106B000087FD02C06AE001C060E0AE014F5F5F4FE9 +:106B1000C2010E9483315AC0AE014F5F5F4F6AE0ED +:106B2000C2010E94E63152C081E0281A31081BC020 +:106B30006624639402C066246A94670C14C0B394FC +:106B4000BB8E11C0EE24EA94ED0C0DC0CC24C3948E +:106B500002C0CC24CA94CF0C06C0A10E04C00F5FA3 +:106B600001C001500E8F8A2D0A2C000C990B9F93A7 +:106B7000AF928C2D0C2C000C990B9F93CF928E2DE5 +:106B80000E2C000C990B9F93EF92EB8D8E2F0E2EF7 +:106B9000000C990B9F93EF93862D062C000C990BFC +:106BA0009F936F923F922F922CE13DE03F932F9362 +:106BB0000E94C5460B8D262DA1016E8DC2010E943B +:106BC00068320FB6F894DEBF0FBECDBF9BE18916C9 +:106BD000910409F017CE80E001C08FEF6E960FB6DA +:106BE000F894DEBF0FBECDBFDF91CF911F910F9103 +:106BF000FF90EF90DF90CF90BF90AF909F908F90DD +:106C00007F906F905F904F903F902F90089560FF1E +:106C100002C0289A01C0289861FF02C0299A01C0C9 +:106C2000299862FF02C02A9A01C02A9863FF02C015 +:106C30002B9A01C02B9864FF02C02C9A01C02C989B +:106C400065FF02C02D9A01C02D9866FF02C02E9AE2 +:106C500001C02E9867FF02C02F9A01C02F98809123 +:106C60008804813019F0823061F0089570FF02C00D +:106C70005E9A01C05E9871FF02C05F9A08955F98A6 +:106C8000089570FF02C0129A01C0129871FF02C0ED +:106C9000139A08951398089580918804813019F00B +:106CA0008230B1F0089560FF02C0109A01C01098C0 +:106CB00061FF02C0119A01C0119862FF02C0129ACE +:106CC00001C0129863FF02C0139A089513980895A3 +:106CD00060FF02C05C9A01C05C9861FF02C05D9ACF +:106CE00001C05D9862FF02C05E9A01C05E9863FFBA +:106CF00002C05F9A08955F980895CF93DF93EC01E7 +:106D000060E070E00E94073660E0CE010E944C36E1 +:106D10008FEF84B980918804813041F0823061F432 +:106D200081B18E6081B98AB1806F05C081B18F60F9 +:106D300081B98AB1806E8AB9DF91CF910895CF93DE +:106D4000DF93EC0160E070E00E94073660E0CE0166 +:106D50000E944C3614B880918804813041F0823012 +:106D600061F481B1817F81B98AB18F7005C081B131 +:106D7000807F81B98AB18F718AB9DF91CF910895EF +:106D800080918804813019F0823039F008956623AB +:106D900011F05D9808955D9A0895662311F0119899 +:106DA0000895119A0895CF92DF92EF92FF920F9378 +:106DB0001F93CF93DF937C01611117C06FE00E9496 +:106DC0004C366FEF73E0C7010E94073661E0C701E0 +:106DD0000E94C03681E09FE09F938F930E94C5463A +:106DE00060ED77E080E090E028C0613069F48DEFDD +:106DF0009EE09F938F930E94C54668EE73E080E00B +:106E000090E00E94AA0A20C0623039F460E071E08C +:106E10000E94073684EF9EE008C06330C1F460E052 +:106E200072E00E9407368BEE9EE09F938F930E9444 +:106E3000C54668EE73E080E090E00E94AA0A60E038 +:106E400070E0C7010E9407360F900F9044C0643273 +:106E500008F043C0C62FD0E024978E0183E015953B +:106E600007958A95E1F7C770DD27CC24C394D12C10 +:106E7000B601002E01C0660F0A94EAF7C7010E940E +:106E80004C360C2E02C0CC0CDD1C0A94E2F7B60185 +:106E9000C7010E940736DF92CF92CC0FDD1FC05E84 +:106EA000DE4F89818F9388818F931F930F9386ED97 +:106EB0009EE09F938F930E94C54660E971E080E059 +:106EC00090E00E94AA0A8DB79EB708960FB6F89474 +:106ED0009EBF0FBE8DBF80E001C08FEFDF91CF91CD +:106EE0001F910F91FF90EF90DF90CF90089581B1A7 +:106EF000807F81B982B18F6082B9089582B1807F2D +:106F000082B981B1807F81B908956130710581F0C6 +:106F100058F06230710581F063307105A1F480B1E1 +:106F200083FB882780F90CC080B18095817008951B +:106F300080B18695817004C080B182FB882780F97A +:106F400091E08927089580E008950F931F93CF93D0 +:106F5000DF93603108F04EC08C01C62FC695C695F0 +:106F6000D0E06370613021F0633009F44BC021C080 +:106F7000BE010E948537811145C0CE0101969F93C5 +:106F80008F9387E19FE09F938F930E94C5460F9058 +:106F90000F900F900F90BE01C8010E94853781119C +:106FA0002BC060E070E0CB010E94AA0A019621F597 +:106FB000F2CFBE010E948537882321F1CE010196D0 +:106FC0009F938F9389E09FE09F938F930E94C54684 +:106FD0000F900F900F900F90BE01C8010E9485374F +:106FE000882351F060E070E0CB010E94AA0A01966C +:106FF00019F4F2CF8FEF07C06AE070E080E090E014 +:107000000E94AA0A80E0DF91CF911F910F9108950D +:10701000CF93C82F8A3019F48DE00E9408388091F0 +:10702000C80085FFFCCFC093CE0080E090E0CF91F8 +:1070300008955A9A22E02093C80028E92093C900B5 +:1070400026E02093CA001092CD002CE02093CC00C3 +:1070500020E331E03093E7042093E60421E0FC01D3 +:1070600022830895FC0112821092C8001092C90078 +:107070001092CA001092CD001092CC001092E7043A +:107080001092E60408950F931F93CF93DF9361113D +:1070900022C009E41FE0C9E2DFE01F930F930E94C2 +:1070A000C546DF93CF938091E7048F938091E604E8 +:1070B0008F930E946C4668E873E180E090E00E9444 +:1070C000AA0A0F900F900F900F900F900F900196BB +:1070D00021F380E001C08FEFDF91CF911F910F91DD +:1070E0000895862F6091E6047091E7040C940838A7 +:1070F0002F923F924F925F926F927F928F929F92C8 +:10710000AF92BF92CF92DF92EF92FF920F931F93B5 +:10711000CF93DF93CDB7DEB760970FB6F894DEBF9D +:107120000FBECDBF7C011B016A01FC0117821682D4 +:10713000838181FF44C39E012F5F3F4F3901F701D7 +:107140009381F10193FD859193FF81911F01882324 +:1071500009F431C3853239F493FD859193FF819110 +:107160001F01853239F4B70190E00E94894656012B +:107170006501E5CF10E0512C912CFFE1F915D8F015 +:107180008B3279F038F4803279F08332A1F4F92D22 +:10719000F0612EC08D3261F0803369F4292D2160B9 +:1071A0002DC0392D3260932E892D8460982E2AC0EF +:1071B000E92DE86015C097FC2DC020ED280F2A307E +:1071C00088F496FE06C03AE0139F200D1124122F7A +:1071D00019C08AE0589E200D1124522EE92DE0623C +:1071E0009E2E10C08E3231F496FCE5C2F92DF0646B +:1071F0009F2E08C08C3621F4292D2068922E02C0C3 +:10720000883641F4F10193FD859193FF81911F012F +:107210008111B3CF9BEB980F933020F4992D90619F +:10722000805E07C09BE9980F933008F066C1992DE6 +:107230009F7E96FF16E09F73992E853619F4906411 +:10724000992E08C0863621F4392F3068932E02C05B +:107250001111115097FE07C01C3350F4442443947D +:10726000410E27E00BC0183038F027E017E005C0CA +:1072700027E09CE3492E02C0212F412C560184E0D7 +:10728000A80EB11CF6016081718182819381042D69 +:10729000A3010E9426456C01F981FC87F0FF02C022 +:1072A000F3FF06C091FC06C092FE06C000E205C0D6 +:1072B0000DE203C00BE201C000E08C858C7019F078 +:1072C00001115AC29BC297FE10C04C0CFC85F4FF02 +:1072D00004C08A81813309F44A94141474F528E0B7 +:1072E000241578F588E0482E2CC096FC2AC0812F02 +:1072F00090E08C159D059CF03CEFC3163FEFD30644 +:1073000074F0892D8068982E0AC0E2E0F0E0EC0F5E +:10731000FD1FE10FF11D8081803319F4115011110F +:10732000F4CF97FE0EC044244394410E812F90E089 +:10733000C816D9062CF41C1904C04424439401C077 +:1073400010E097FE06C01C141D0434F4C60101961B +:1073500005C085E090E002C081E090E00111019657 +:10736000112331F0212F30E02F5F3F4F820F931F09 +:10737000252D30E08217930714F4581A01C0512CC0 +:10738000892D897049F4552039F0B70180E290E0E9 +:107390000E9489465A94F7CF002329F0B701802F25 +:1073A00090E00E94894693FC09C0552039F0B7014E +:1073B00080E390E00E9489465A94F7CF97FE4CC034 +:1073C0004601D7FE02C0812C912CC601881999096B +:1073D000F301E80FF91FFE87ED87960124193109A3 +:1073E000388B2F87012F10E01195019511093FEF80 +:1073F0008316930629F4B7018EE290E00E94894635 +:10740000C814D9044CF08F8598898815990524F4FF +:10741000ED85FE85818101C080E3F1E08F1A91083E +:107420002D853E852F5F3F4F3E872D878016910625 +:107430002CF0B70190E00E948946D9CFC814D90436 +:1074400041F49A81963320F4953319F43C8534FF46 +:1074500081E3B70190E04EC08A81813319F09C85A9 +:107460009F7E9C87B70190E00E948946111105C05C +:1074700094FC18C085E690E017C0B7018EE290E05A +:107480000E9489461E5F82E001E0080FF301E80FC9 +:10749000F11D8081B70190E00E948946802F011381 +:1074A000F3CFE6CF85E490E0B7010E948946D7FC90 +:1074B00006C0C114D10441F4EC85E4FF05C0D194A9 +:1074C000C194D1088DE201C08BE2B70190E00E9427 +:1074D000894680E32AE0C216D1042CF08F5FFAE0DF +:1074E000CF1AD108F7CFB70190E00E948946B701C3 +:1074F000C601C0960E94894654C1833631F0833755 +:1075000079F0833509F056C020C0560132E0A30E51 +:10751000B11CF6018081898301E010E0630112C093 +:107520005601F2E0AF0EB11CF601C080D18096FE8C +:1075300003C0612F70E002C06FEF7FEFC6010E94B1 +:107540002F468C01F92DFF7714C0560122E0A20EC0 +:10755000B11CF601C080D18096FE03C0612F70E09F +:1075600002C06FEF7FEFC6010E9407468C01F92D24 +:10757000F0689F2EF3FD1AC0852D90E008171907BB +:10758000A8F4B70180E290E00E9489465A94F4CFB3 +:10759000F60197FC859197FE81916F01B70190E00C +:1075A0000E94894651105A94015011090115110584 +:1075B00079F7F7C0843611F0893661F5560197FEE8 +:1075C00009C024E0A20EB11CF601608171818281A4 +:1075D00093810AC0F2E0AF0EB11CF60160817181A7 +:1075E000072E000C880B990BF92DFF769F2E97FF25 +:1075F00009C090958095709561957F4F8F4F9F4FF3 +:10760000F0689F2E2AE030E0A3010E940F47C82EA9 +:10761000C6183FC0092D853721F40F7E2AE030E0DF +:107620001DC0097F8F3691F018F4883559F0C3C01A +:10763000803719F0883711F0BEC0006104FF09C01F +:10764000046007C094FE08C0066006C028E030E071 +:1076500005C020E130E002C020E132E0560107FF22 +:1076600009C084E0A80EB11CF6016081718182819D +:10767000938108C0F2E0AF0EB11CF6016081718108 +:1076800080E090E0A3010E940F47C82EC6180F7734 +:10769000902E96FE0BC0092D0E7FC11650F494FE5D +:1076A0000AC092FC08C0092D0E7E05C0DC2C092DF5 +:1076B00003C0DC2C01C0D12E04FF0DC0FE01EC0D77 +:1076C000F11D8081803311F4097E09C002FF06C0DC +:1076D000D394D39404C0802F867809F0D39403FD0B +:1076E00011C000FF06C01C2DD51480F4150D1D1906 +:1076F0000DC0D51458F4B70180E290E00E9489468D +:10770000D394F7CFD51410F45D1801C0512C04FFA9 +:1077100010C0B70180E390E00E94894602FF17C0C5 +:1077200001FD03C088E790E002C088E590E0B70162 +:107730000CC0802F867859F001FF02C08BE201C097 +:1077400080E207FD8DE2B70190E00E948946C116F4 +:1077500038F4B70180E390E00E9489461150F7CFDA +:10776000CA94F301EC0DF11D8081B70190E00E94F5 +:107770008946C110F5CF15C0F4E0F51560F584E039 +:10778000581A93FE1FC0011127C02C8523FF2AC061 +:107790000EE011E0392D3071932EF80184918111A2 +:1077A00024C0552009F4E4CCB70180E290E00E94A7 +:1077B00089465A94F6CFF7018681978126C08FEFCC +:1077C0009FEF23C0B70180E290E00E9489465A945F +:1077D0005110F8CFD8CF512CB701802F90E00E94E4 +:1077E0008946D3CF02E111E0D5CF91108052B70185 +:1077F00090E00E9489460F5F1F4FCFCF23E02515F1 +:1078000010F483E0BDCF512CC0CF60960FB6F89432 +:10781000DEBF0FBECDBFDF91CF911F910F91FF90C3 +:10782000EF90DF90CF90BF90AF909F908F907F9020 +:107830006F905F904F903F902F9008950F931F93FC +:10784000CF93DF93EC018B0180EA91E099838883E9 +:10785000CE0104960E94C60CCE01CD960E940F1652 +:10786000CE0187589F4F0E94E607CE0185589F4F53 +:107870000E944C09FE01E057FF4F10821B830A83D0 +:10788000DF91CF911F910F910895CF93DF93CDB7E3 +:10789000DEB7AE970FB6F894DEBF0FBECDBF87B18F +:1078A0008C6987B988B1837688B93D98459A82E01A +:1078B0008093C00098E99093C10096E09093C20035 +:1078C0001092C5009CE09093C4008093B000809318 +:1078D000B10096E99093B3008093700080E191E04D +:1078E0009093E5048093E40482E091E09093E304B4 +:1078F0008093E20478940E944D0A86E2EEE3F1E080 +:10790000DE01119601900D928A95E1F7809188042D +:10791000811102C087E001C083E1FAE0CF2EF3E0DD +:10792000DF2EACE0AA2EA3E0BA2E682E712C4E01F9 +:1079300027E2820E911CCE018B969EA78DA762E84E +:10794000262E62E0362E7AE4472E73E0572EEEE7BD +:10795000EE2EE2E0FE2EDF92CF920E94C546BF924D +:10796000AF920E94C546A5E3B3E0BF93AF930E94D8 +:10797000C5463F922F920E94C5465F924F920E9449 +:10798000C546FF92EF920E94C54681E493E09F9323 +:107990008F930E94C546BF92AF920E94C5468091C8 +:1079A00088049FEF980F0FB6F894DEBF0FBECDBFCF +:1079B000923078F40E94400A9F938F938EE692E073 +:1079C0009F938F930E94C5460F900F900F900F903A +:1079D00008C086E592E09F938F930E94C5460F9062 +:1079E0000F90BF92AF920E94C546DF92CF920E9445 +:1079F000C54683E492E09F938F930E94C5461CA6E0 +:107A00001BA60F900F900F900F900F900F908BA5CB +:107A10009CA58615970570F5FC01EE0FFF1F21E070 +:107A200030E02C0F3D1FE20FF31F008111819F9367 +:107A30008F938AE392E09F938F930E94C546D8016B +:107A4000ED91FC910680F781E02DC80109959F9387 +:107A50008F930E94C546DF92CF920E94C5468BA5A8 +:107A60009CA501969CA78BA70FB6F894DEBF0FBE0E +:107A7000CDBFCDCF8BE292E09F938F930E94C546FE +:107A800010925F0410925E044091E2045091E3046E +:107A900064E070E0C4010E943A463EA53F938DA584 +:107AA0008F9388E791E09F938F939F928F920E948C +:107AB000ED460FB6F894DEBF0FBECDBF019709F0BB +:107AC0004ACF8BA59CA58615970508F044CF880F53 +:107AD000991FE1E0F0E0EC0FFD1FE80FF91F0081B6 +:107AE000118187E292E09F938F930E94C546D8014F +:107AF000ED91FC910680F781E02DC80109959F93D7 +:107B00008F930E94C54683E292E09F938F930E94D9 +:107B1000C5468FEF9FEF9093010180930001D8013C +:107B2000ED91FC910280F381E02DC80109950F9041 +:107B30000F900F900F900F900F90E12CF12C3CE1E3 +:107B4000432E32E0532E22242A94322CFF92EF92BD +:107B50005F924F920E94C546D801ED91FC91019031 +:107B6000F081E02D6E2DC80109950F900F900F90B8 +:107B70000F9087FD18C080910001909101014B97F3 +:107B800039F48FEF9FEF90930101809300010BC0B8 +:107B90003092010120920001BFEFEB1AFB0A2FEF98 +:107BA000E216F10499F6D801ED91FC910480F5817B +:107BB000E02DC8010995C3CE84EC91E090935D045B +:107BC00080935C0481E492E090935B0480935A0478 +:107BD0008CE092E0909359048093580484E292E000 +:107BE00090935604809355041092570484EF91E0CB +:107BF000909354048093530482E991E0909350044D +:107C000080934F04109252041092510480E092E04D +:107C100090934E0480934D048CED91E09093490431 +:107C20008093480410924A0410924C0410924B0422 +:107C300088EE91E0909347048093460488EB91E0AE +:107C4000909343048093420481E0909188049130A2 +:107C500009F480E080934404109245048DE492E09E +:107C60009093400480933F041092410480ED91E092 +:107C700090932C0480932B0410922D0410922E04C8 +:107C80008CEA91E0909327048093260410922804B4 +:107C90008FEF9FEF90932A048093290460E070E0B7 +:107CA00085E993E00E941E3C61E070E084E093E08F +:107CB0000E941E3C62E070E083E792E00E941E3C5E +:107CC00088E192E090936D0280936C028EE692E0E0 +:107CD0000E94E607109270021092720210927102D6 +:107CE00086E891E0909362028093610210926A02AA +:107CF00021E030E030936902209368021092630221 +:107D0000109264021092650210926602109267024D +:107D1000909357028093560210925F0282E090E0A7 +:107D200090935E0280935D02109258021092590265 +:107D300010925A0210925B0210925C0208958AE936 +:107D400094E00E94C60C88E191E09093990480939E +:107D5000980490939704809396049093950480934D +:107D6000940408958CE497E0089580E399E00895E1 +:107D700087E699E0089588E799E0089589E59AE013 +:107D8000089587EE9AE0089580E99BE008958CEBD2 +:107D90009BE0089585E09CE008958CE19CE00895C7 +:107DA00084E09FE0089582E29FE0089589E69FE0E5 +:107DB0000895DB018F939F930E94363FBF91AF914F +:107DC000A29F800D911DA39F900DB29F900D112435 +:107DD0000895991B79E004C0991F961708F0961B27 +:107DE000881F7A95C9F78095089587FB082E062687 +:107DF00087FD819567FD61950E94E93E0EF491959E +:107E000007FC81950895AA1BBB1B51E107C0AA1F5F +:107E1000BB1FA617B70710F0A61BB70B881F991F2B +:107E20005A95A9F780959095BC01CD010895052E2E +:107E300097FB1EF400940E942E3F57FD07D00E942E +:107E4000DA4007FC03D04EF40C942E3F5095409539 +:107E5000309521953F4F4F4F5F4F089590958095F6 +:107E6000709561957F4F8F4F9F4F08950E940541F8 +:107E7000A59F900DB49F900DA49F800D911D11247E +:107E80000895B7FF0C94363F0E94363F821B930B38 +:107E90000895DF93CF931F930F939A9DF02D219F09 +:107EA000F00D8B9DF00D8A9DE02DF10D039FF00DDF +:107EB000029FE00DF11D4E9DE00DF11D5E9DF00D48 +:107EC0004F9DF00D7F936F93BF92AF925F934F934F +:107ED000D5010E9405418B01AC01D7010E940541EB +:107EE000EB01E80FF91FD6010E94993F2F913F91B6 +:107EF000D6010E940541C60FD71FE81FF91FAF9199 +:107F0000BF910E94993F2F913F910E940541C60F5A +:107F1000D71FE81FF91FD6010E940541E60FF71F82 +:107F20009801BE01CF0111240F911F91CF91DF91D4 +:107F300008950E940541460F571FC81FD91F08F416 +:107F400031960895689401C0E894F92FF12B12F04E +:107F50000C94D63FA0E0B0E0E0EBFFE30C94484087 +:107F6000092E059422F40E943240112392F4F0E885 +:107F70000F26FFEFE094F09400951095B094C09414 +:107F8000D094A194BF0ACF0ADF0AEF0AFF0A0F0BB1 +:107F90001F0B0E94E13F07FC0E943240CDB7DEB7C5 +:107FA000ECE00C946440689401C0E8948F929F9236 +:107FB000CF93DF930E94E13FDF91CF919F908F900D +:107FC000089588249924F401E401B0E49F93AA273A +:107FD0009A158B049C04ED05FE05CF05D007A1077B +:107FE00098F4AD2FDC2FCF2FFE2FE92D982C892E62 +:107FF000982F872F762F652F542F432F322F22272C +:10800000B85031F7BF9127C01B2EBF91BB27220F5D +:10801000331F441F551F661F771F881F991F881C19 +:10802000991CEE1FFF1FCC1FDD1FAA1FBB1F8A1448 +:108030009B04EC05FD05CE05DF05A007B10748F060 +:108040008A189B08EC09FD09CE09DF09A00BB10BCA +:1080500021601A94E1F62EF49401AF01BE01CD0126 +:10806000000C089560957095809590953095409599 +:10807000509521953F4F4F4F5F4F6F4F7F4F8F4F21 +:108080009F4F08952F923F924F925F926F927F92EF +:108090008F929F92AF92BF92CF92DF92EF92FF9218 +:1080A0000F931F93CF93DF93CDB7DEB7CA1BDB0BC4 +:1080B0000FB6F894DEBF0FBECDBF09942A88398869 +:1080C00048885F846E847D848C849B84AA84B98470 +:1080D000C884DF80EE80FD800C811B81AA81B9817C +:1080E000CE0FD11D0FB6F894DEBF0FBECDBFED0190 +:1080F00008950F93083090F0982F872F762F652FD3 +:10810000542F432F322F22270850F4CF220F331F32 +:10811000441F551F661F771F881F991F0A95B2F7C6 +:108120000F91089597FB10F8169400080F930830EC +:1081300098F00850232F342F452F562F672F782F74 +:10814000892F902DF4CF059497958795779567950E +:1081500057954795379527950A95AAF70F91089552 +:108160002A0D3B1D4C1D5D1D6E1D7F1D801F911F27 +:1081700008950024A7FD00942A0F301D401D501DB6 +:10818000601D701D801D901D08952A193B094C0922 +:108190005D096E097F09800B910B08950024A7FDEE +:1081A00000942A17300540055005600570058005CC +:1081B00090050895A1E21A2EAA1BBB1BFD010DC05C +:1081C000AA1FBB1FEE1FFF1FA217B307E407F50787 +:1081D00020F0A21BB30BE40BF50B661F771F881F63 +:1081E000991F1A9469F760957095809590959B01F9 +:1081F000AC01BD01CF010895EE0FFF1F0024001C4C +:108200000BBE0790F691E02D0994A29FB001B39F99 +:10821000C001A39F700D811D1124911DB29F700D8F +:10822000811D1124911D08955058BB27AA270E9433 +:108230002C410C94D1420E94C34238F00E94CA42A1 +:1082400020F039F49F3F19F426F40C94B0420EF458 +:10825000E095E7FB0C94AA42E92F0E94E24258F312 +:10826000BA17620773078407950720F079F4A6F51B +:108270000C9404430EF4E0950B2EBA2FA02D0B01A5 +:10828000B90190010C01CA01A0011124FF27591B5B +:1082900099F0593F50F4503E68F11A16F040A22F61 +:1082A000232F342F4427585FF3CF469537952795D2 +:1082B000A795F0405395C9F77EF41F16BA0B620BD1 +:1082C000730B840BBAF09150A1F0FF0FBB1F661F18 +:1082D000771F881FC2F70EC0BA0F621F731F841F5B +:1082E00048F4879577956795B795F7959E3F08F081 +:1082F000B0CF9395880F08F09927EE0F9795879543 +:1083000008950E9495410C94D1420E94CA4258F0AF +:108310000E94C34240F029F45F3F29F00C94AA4226 +:1083200051110C9405430C94B0420E94E24268F350 +:108330009923B1F3552391F3951B550BBB27AA271E +:1083400062177307840738F09F5F5F4F220F331F58 +:10835000441FAA1FA9F335D00E2E3AF0E0E832D020 +:1083600091505040E695001CCAF72BD0FE2F29D023 +:10837000660F771F881FBB1F261737074807AB07F5 +:10838000B0E809F0BB0B802DBF01FF2793585F4F6A +:108390003AF09E3F510578F00C94AA420C940543A4 +:1083A0005F3FE4F3983ED4F3869577956795B7954C +:1083B000F7959F5FC9F7880F911D9695879597F957 +:1083C0000895E1E0660F771F881FBB1F62177307D0 +:1083D0008407BA0720F0621B730B840BBA0BEE1FE5 +:1083E00088F7E09508950E94FA416894B1110C94C1 +:1083F000054308950E94EA4288F09F5798F0B92FEC +:108400009927B751B0F0E1F0660F771F881F991FC9 +:108410001AF0BA95C9F714C0B13091F00E94044324 +:10842000B1E008950C940443672F782F8827B85F34 +:1084300039F0B93FCCF3869577956795B395D9F721 +:108440003EF490958095709561957F4F8F4F9F4F2B +:10845000089597FB16F40E94B3420C946D42E89481 +:1084600009C097FB3EF490958095709561957F4F7C +:108470008F4F9F4F9923A9F0F92F96E9BB2793952A +:10848000F695879577956795B795F111F8CFFAF43A +:10849000BB0F11F460FF1BC06F5F7F4F8F4F9F4F6B +:1084A00016C0882311F096E911C0772321F09EE8C9 +:1084B000872F762F05C0662371F096E8862F70E02F +:1084C00060E02AF09A95660F771F881FDAF7880F09 +:1084D0009695879597F90895E894F92F96EBFF23E1 +:1084E00081F0121613061406440B9395F6958795A2 +:1084F0007795679557954040FF23B9F71BC099279B +:108500000895882351F49850D2F7872B762F652F42 +:10851000542F432F322F20E0B1F312161306140606 +:10852000440B88233AF09A95440F551F661F771F16 +:10853000881FCAF755234AF4440F551F11F460FFF2 +:1085400004C06F5F7F4F8F4F9F4F880F9695879521 +:1085500097F9089597F99F6780E870E060E00895C3 +:108560009FEF80EC0895909580957095609550955B +:108570004095309521953F4F4F4F5F4F6F4F7F4F45 +:108580008F4F9F4F089500240A9416161706180659 +:108590000906089500240A94121613061406050607 +:1085A0000895092E0394000C11F4882352F0BB0F98 +:1085B00040F4BF2B11F460FF04C06F5F7F4F8F4FFB +:1085C0009F4F089557FD9058440F551F59F05F3F36 +:1085D00071F04795880F97FB991F61F09F3F79F0E5 +:1085E00087950895121613061406551FF2CF469567 +:1085F000F1DF08C0161617061806991FF1CF8695E9 +:108600007105610508940895E894BB2766277727CC +:10861000CB0197F908950E941E430C94D1420E9409 +:10862000C34238F00E94CA4220F0952311F00C9406 +:10863000AA420C94B04211240C9405430E94E242D9 +:1086400070F3959FC1F3950F50E0551F629FF001A5 +:10865000729FBB27F00DB11D639FAA27F00DB11DBE +:10866000AA1F649F6627B00DA11D661F829F222747 +:10867000B00DA11D621F739FB00DA11D621F839FCE +:10868000A00D611D221F749F3327A00D611D231FA4 +:10869000849F600D211D822F762F6A2F11249F57F2 +:1086A00050409AF0F1F088234AF0EE0FFF1FBB1FF5 +:1086B000661F771F881F91505040A9F79E3F5105B4 +:1086C00080F00C94AA420C9405435F3FE4F3983E7B +:1086D000D4F3869577956795B795F795E7959F5F5E +:1086E000C1F7FE2B880F911D9695879597F90895F0 +:1086F000FA01EE0FFF1F309621053105A1F161153A +:10870000710561F48038BFE39B0749F168949038A4 +:10871000810561F08038BFEF9B0741F099234AF54E +:10872000FF3FE1053105210519F1E8940894E7952B +:10873000D901AA2329F4AB2FBE2FF85FD0F310C0C4 +:10874000FF5F70F4A695E0F7F73950F019F0FF3AA3 +:1087500038F49F779F930DD00F9007FC90580895A1 +:1087600046F00C94B04260E070E080E89FE308952A +:108770004FE79F775F934F933F932F930E949C44C3 +:108780002F913F914F915F910E940B430C94D543E1 +:108790000E940744880B990B089529F416F00C9455 +:1087A000AA420C9404430C94B0420E94EA42A8F3FB +:1087B0009638A0F707F80F92E8942BE33AEA48EBD3 +:1087C0005FE70E9421430F920F920F924DB75EB761 +:1087D0000F920E94E444ECE8F0E00E942B444F9199 +:1087E0005F91EF91FF91E595EE1FFF1F49F0FE5756 +:1087F000E0684427EE0F441FFA95E1F74195550BC9 +:108800000E945E440F9007FE0C9452440895990F05 +:108810000008550FAA0BE0E8FEEF16161706E8074A +:10882000F907C0F012161306E407F50798F0621B6B +:10883000730B840B950B39F40A2661F0232B242B40 +:10884000252B21F408950A2609F4A140A6958FEF5F +:10885000811D811D0895DF93CF931F930F93FF9286 +:10886000EF92DF927B018C01689406C0DA2EEF0153 +:108870000E941E43FE01E894A591259135914591F2 +:108880005591A6F3EF010E942C41FE019701A8012A +:10889000DA9469F7DF90EF90FF900F911F91CF91DD +:1088A000DF9108959B01AC0160E070E080E89FE3F8 +:1088B0000C9481410C94AA420C9418450E94EA42FF +:1088C000D8F39923C9F3940F511DA3F3915050404D +:1088D00094F059F0882332F0660F771F881F91506B +:1088E0005040C1F79E3F51052CF7880F911D96957A +:1088F000879597F908955F3FACF0983E9CF0BB27B1 +:10890000869577956795B79508F4B1609395C1F70B +:10891000BB0F58F711F460FFE8CF6F5F7F4F8F4FA9 +:108920009F4FE3CF0C94054316F00C9418450C941C +:10893000B04268940C94AA420E94EA42A8F3992398 +:10894000C1F3AEF3DF93CF931F930F93FF92C92F21 +:10895000DD2788232AF02197660F771F881FDAF713 +:1089600020E030E040E85FEB9FE3883920F0803E74 +:1089700038F021968F770E941541E4EBF0E004C0B7 +:108980000E941541E1EEF0E00E942B448B01BE01F4 +:10899000EC01FB2E6F5771097595771F880B990BAA +:1089A0000E94314228E132E741E35FE30E941E4327 +:1089B000AF2D9801AE01FF900F911F91CF91DF91E4 +:1089C0000E942C410C94D142FA01DC01AA0FBB1F7A +:1089D0009B01AC01BF5728F4222733274427507846 +:1089E00020C0B75190F4AB2F0024469537952795BA +:1089F000011CA395D2F3002071F0220F331F441FF6 +:108A0000B395DAF30ED00C94144161307105A0E8EF +:108A10008A07B94630F49B01AC016627772788277F +:108A20009078309621F020833183428353830895D8 +:108A30009F3F31F0915020F4879577956795B795D2 +:108A4000880F911D9695879597F90895283008F01D +:108A500027E03327DA01990F311D87FD91600096D9 +:108A60006105710539F432602E5F3D9330E32A953C +:108A7000E1F708959F3F30F080387105610509F0F6 +:108A80003C5F3C5F3D93913008F08068911DDF931F +:108A9000CF931F930F93FF92EF92192F987F969584 +:108AA000E92F96959695E90FFF27E059FE4F9927F4 +:108AB0003327EE24FF24A701E701059008940794CB +:108AC00028F4360FE71EF81E491F511D660F771F49 +:108AD000881F991F0694A1F70590079428F4E70EC4 +:108AE000F81E491F561FC11D770F881F991F661F4B +:108AF0000694A1F70590079428F4F80E491F561F15 +:108B0000C71FD11D880F991F661F771F0694A1F7F5 +:108B10000590079420F4490F561FC71FD81F990FBF +:108B2000661F771F881F0694A9F784911095177008 +:108B300041F0D695C79557954795F794E7941A95C0 +:108B4000C1F7E6E1F1E068941590159135916591D2 +:108B5000959105907FE27395E118F10A430B560B4E +:108B6000C90BD009C0F7E10CF11E431F561FC91FE6 +:108B7000D01D7EF4703311F48A95E6CFE89401504D +:108B800030F0080F0AF40027021708F4202F23956D +:108B9000022F7A3328F079E37D932A95E9F710C004 +:108BA0007D932A9589F6069497956795379517953D +:108BB0001794E118F10A430B560BC90BD00998F032 +:108BC00023957E9173957A3308F070E37C9320139C +:108BD000B8F77E9170617D9330F0839571E37D935A +:108BE00070E32A95E1F71124EF90FF900F911F9108 +:108BF000CF91DF91992787FD90950895FB01DC01C6 +:108C000002C005900D9241505040D8F70895FC01E4 +:108C10000590615070400110D8F7809590958E0FA7 +:108C20009F1F0895FB01DC012150304030F001907E +:108C30000D920416C9F7CD01089588279927089544 +:108C4000FB01DC014150504048F001900D920020A2 +:108C5000C9F701C01D9241505040E0F70895FC0152 +:108C60006150704001900110D8F7809590958E0F5B +:108C70009F1F0895CF92DF92EF92FF920F931F9361 +:108C8000CF93DF93FA01238120FD03C080E090E0C1 +:108C90001AC016161706D4F77A018C01EB016C0185 +:108CA000C130D10569F0C7010E94654A8F3FFFEFCF +:108CB0009F0761F3F60181936F0121970A9781F76E +:108CC000F6011082C801DF91CF911F910F91FF90A3 +:108CD000EF90DF90CF9008950F931F93CF93DF9382 +:108CE000CDB7DEB70F811885F80183818860838353 +:108CF000AE01455F5F4F69857A85C8010E9478386B +:108D0000F8012381277F2383DF91CF911F910F915A +:108D100008950F931F93CF93DF93FB01238121FDD0 +:108D200003C08FEF9FEF2CC022FF16C046815781F2 +:108D3000248135814217530744F4A081B1819D01FC +:108D40002F5F3F4F318320838C93268137812F5FA4 +:108D50003F4F3783268314C08B01EC01FB01008455 +:108D6000F185E02D0995892BE1F6D80116968D91B4 +:108D70009C911797019617969C938E931697CE0108 +:108D8000DF91CF911F910F9108950F931F93CF9370 +:108D9000DF93CDB7DEB7AE01495F5F4FDA016D916A +:108DA0007D91AD0102EE14E0F80182819381DC0136 +:108DB00013962C911397286013962C930E94783861 +:108DC000D8011296ED91FC9113972381277F23837D +:108DD000DF91CF911F910F910895CF93DF93CDB77E +:108DE000DEB72E970FB6F894DEBF0FBECDBF85E07D +:108DF0008C838B899C899A838983AE01495E5F4FFE +:108E00006D897E89CE0101960E942C492E960FB65F +:108E1000F894DEBF0FBECDBFDF91CF910895FA0168 +:108E2000AA27283051F1203181F1E8946F936E7FA9 +:108E30006E5F7F4F8F4F9F4FAF4FB1E03ED0B4E09A +:108E40003CD0670F781F891F9A1FA11D680F791FDB +:108E50008A1F911DA11D6A0F711D811D911DA11DEC +:108E600020D009F468943F912AE0269F11243019FC +:108E7000305D3193DEF6CF010895462F4770405D97 +:108E80004193B3E00FD0C9F7F6CF462F4F70405D46 +:108E90004A3318F0495D31FD4052419302D0A9F7A1 +:108EA000EACFB4E0A6959795879577956795BA959B +:108EB000C9F700976105710508959B01AC010A2E61 +:108EC00006945795479537952795BA95C9F7620F38 +:108ED000731F841F951FA01D089520FD09C0FC016C +:108EE00023FD05C022FF02C0738362835183408348 +:108EF000089544FD17C046FD17C0AB01BC01DA015F +:108F0000FB01AA0FBB1FEE1FFF1F1094D1F74A0FE2 +:108F10005B1F6E1F7F1FCB01BA01660F771F881F73 +:108F2000991F09C033E001C034E0660F771F881F26 +:108F3000991F3150D1F7620F711D811D911D089548 +:108F40000F931F93CF93DF938C01C8010E94654A52 +:108F5000EC0197FD08C00E94514A892BB1F7B80176 +:108F6000CE010E94A34ACE01DF91CF911F910F91B4 +:108F700008958F929F92AF92BF92EF92FF920F93BC +:108F80001F93CF93DF938C01D62F7A01B22E0E94CC +:108F9000654A9C0133272B32310531F02D323105E2 +:108FA00061F48B2D8068B82ED15011F480E068C038 +:108FB000C8010E94654A97FDF9CFCB2DCD7F2B2D9F +:108FC000207309F58033F9F4AA24AA94AD0E09F4AC +:108FD00043C0C8010E94654A97FD3EC09C012F7D99 +:108FE00033272835310549F4C264D250A9F1C801AC +:108FF0000E94654A97FF07C02FC0B6FE02C0C2603C +:1090000001C0C261DA2D812C912C540120ED280F72 +:10901000283080F0C4FF04C0B8010E94A34A19C0E0 +:109020002A3040F0C6FFF8CF2F7D3FEE320F3630AA +:1090300098F727504C2FC501B4010E9479474B0186 +:109040005C01C260D15059F0C8010E94654A97FF87 +:10905000DDCFC1FD04C0AACF812C912C5401C7FFE4 +:1090600008C0B094A09490948094811C911CA11C81 +:10907000B11C2C2FB501A401C7010E946D4781E0EE +:10908000DF91CF911F910F91FF90EF90BF90AF9024 +:109090009F908F9008955F926F927F928F929F9290 +:1090A000AF92BF92CF92DF92EF92FF920F931F93F6 +:1090B000CF93DF93CDB7DEB7A0970FB6F894DEBF9E +:1090C0000FBECDBF5C01962E7A01F9018E010F5FB4 +:1090D0001F4F680180E2D8011D928A95E9F7D501FA +:1090E00013968C9080E090E0612C712C30E061E070 +:1090F00070E083FC259183FE21918F01522E211176 +:1091000003C080E090E092C02E3511F4009751F139 +:10911000432F50E0481759073CF42D3559F12D32B3 +:1091200019F4772009F103C0772009F46AC0452DAE +:10913000469546954695D601A40FB11D452D47701D +:109140008B0102C0000F111F4A95E2F7A8015C9144 +:10915000452B4C93651459F0561410F45394E7CFF3 +:109160005A94E5CF31E004C07724739401C0712C88 +:109170000196BFCF772019F08E8180628E833111E6 +:1091800003C08824839417C0F6019E012F5D3F4FD2 +:109190008081809581932E173F07D1F7F2CFE1149C +:1091A000F10429F0D7018C93F70131967F019A944D +:1091B000812C9920F9F0C5010E94654A97FD18C0DD +:1091C000FC01FF2723E0F595E7952A95E1F7EC0DE3 +:1091D000FD1D208130E0AC014770552702C0359558 +:1091E00027954A95E2F720FDDACFB5010E94A34A00 +:1091F000811087CFE114F10411F0D7011C92C8014E +:1092000015C0422F469546954695D601A40FB11D2F +:10921000422F47708B0102C0000F111F4A95E2F7E1 +:10922000A8015C91452B4C93622EA2CFA0960FB65D +:10923000F894DEBF0FBECDBFDF91CF911F910F918C +:10924000FF90EF90DF90CF90BF90AF909F908F9066 +:109250007F906F905F9008955F926F927F928F9250 +:109260009F92AF92BF92CF92DF92EF92FF920F93B5 +:109270001F93CF93DF936C01EB015A01FC0117821E +:109280001682512CF601E380FE01E3FC8591E3FE9A +:109290008191182FEF01882309F4EEC090E00E941D +:1092A000514A892B21F0C6010E94A047EBCF15320D +:1092B00041F4FE01E3FC1591E3FE1191EF0115323B +:1092C00081F4C6010E94654A97FDD4C0412F50E049 +:1092D0009C01332724173507A9F2B6010E94A34A3F +:1092E000CBC01A3239F4E3FC1591E3FE1191EF0182 +:1092F00001E001C000E0F12C20ED210F2A3080F4C4 +:1093000002606F2D70E080E090E040E20E947947BB +:10931000F62EFE01E3FC1591E3FE1191EF01ECCF77 +:1093200001FF03C0F11003C0A7C0FF24FA94183650 +:1093300019F01C3651F010C0FE01E3FC1591E3FE5C +:109340001191EF01183641F408600460FE01E3FC5E +:109350001591E3FE1191EF01112309F48DC0612FE6 +:1093600070E080E192E00E945A4A892B09F484C09F +:1093700000FD07C0F50180809180C50102965C0167 +:1093800002C0812C912C1E3651F4F6014681578182 +:1093900060E070E0202FC4010E946D4773CF133648 +:1093A000A9F401FD02C0FF24F394C6010E94654A9E +:1093B00097FD60C08114910429F0F4018083C401F9 +:1093C00001964C01FA94F110F0CF50C01B3559F4BE +:1093D0009E01A4016F2DC6010E944B48EC01892B10 +:1093E00009F044C03EC0C6010E94A04797FD42C09C +:1093F0001F3661F128F4143639F1193651F128C0BD +:10940000133771F0153701F123C08114910429F04D +:10941000F4016082C40101964C01FA94FF2071F0BE +:10942000C6010E94654A3C0197FD08C00E94514A4E +:10943000892B59F3B601C3010E94A34A81149104F8 +:10944000A9F0F401108212C0006203C0006101C0E3 +:109450000064202FA4016F2DC6010E94B94781111D +:1094600005C0F6018381807329F406C000FD0ACF90 +:10947000539408CF552019F0852D90E002C08FEF4E +:109480009FEFDF91CF911F910F91FF90EF90DF90B1 +:10949000CF90BF90AF909F908F907F906F905F9094 +:1094A000089591110C94BC4A803219F0895085506E +:1094B000C8F70895FC010590061621F00020D9F7A1 +:1094C000C00108953197CF010895CF93DF93EC0148 +:1094D0002B8120FF33C026FF0AC02F7B2B838E8178 +:1094E0009F8101969F838E838A8190E029C022FF0D +:1094F0000FC0E881F9818081082E000C990B00973C +:1095000019F420622B831AC03196F983E8830EC0C8 +:10951000EA85FB85099597FF09C02B81019611F01B +:1095200080E201C080E1822B8B8308C02E813F81C5 +:109530002F5F3F4F3F832E83992702C08FEF9FEF0E +:10954000DF91CF910895FB01238120FF12C026FDFA +:1095500010C08F3F3FEF930761F082832F7D20641F +:109560002383268137812150310937832683992728 +:1095700008958FEF9FEF0895992788270895F8940D +:02958000FFCF1B +:10958200FFFF00000001000000000000CE09000003 +:109592000000000200000000EF09000000000303C9 +:1095A2002C022E02300232023402360238023A0211 +:1095B20000000002000000000838000000003F0424 +:1095C2000403730295036C02610256025C045A049E +:1095D2005804550453044F044D04480446044204FD +:1095E2002B042604280545053505E308D808CE08CE +:1095F20001040000000271CB257800424D4532384B +:109602003000000000000D215C22521A411A0000B5 +:109612000000D822C522CF22B23E3F000000000047 +:10962200A1237C2393236B2300000000F926C32689 +:10963200E126B53E000000007D29B7282D29B83E5D +:1096420000000000502AFA29012ABB3E0000000057 +:10965200A82A932A942ABE3E000000007C2C622C89 +:10966200492CC13E00000000802EF02D002EC43E89 +:10967200000000008E2F822F892FC73E00000000BD +:10968200E22FD62FDD2FCA3E00000000E030B230BC +:10969200C930CD3E000000006B33553166316E316A +:1096A20000000000D3367D369F36D03E4100420096 +:1096B20043004400450046004700445000000000BB +:1096C20000A53777377E37D33E00000000433819B4 +:0696D200383238D63E00DC +:00000001FF diff --git a/software/deye-sun-12k/nano-1284/src b/software/deye-sun-12k/nano-1284/src new file mode 120000 index 0000000..5cd551c --- /dev/null +++ b/software/deye-sun-12k/nano-1284/src @@ -0,0 +1 @@ +../src \ No newline at end of file diff --git a/software/deye-sun-12k/nano-644/Makefile b/software/deye-sun-12k/nano-644/Makefile new file mode 100644 index 0000000..7b20d98 --- /dev/null +++ b/software/deye-sun-12k/nano-644/Makefile @@ -0,0 +1,262 @@ +$(shell mkdir -p dist >/dev/null) +$(shell mkdir -p build >/dev/null) +$(shell mkdir -p sim >/dev/null) +$(shell mkdir -p sim/build >/dev/null) +$(shell mkdir -p release/sim >/dev/null) + +# -------------------------------------------------------------------------------- +# Variables configured by engineer + +NAME=nano-x-base_deye-sun-12k_nano-m644p_12mhz +DEVICE=atmega644p +AVRDUDE_DEVICE=m644p +CPU_FREQUENCY=12000000 +BAUDRATE=115200 +START_ADDRESS=0 + +# -------------------------------------------------------------------------------- +# Automatic created Makefile variables + +SRC= $(wildcard src/*.c src/*.cpp src/*/*.c src/*/*.cpp) +HDR= $(wildcard src/*.h src/*.hpp src/*/*.h src/*/*.hpp) +MAINSRC= $(wildcard src/main.c src/main.cpp) +OBJ_CPP = $(SRC:src/%.cpp=build/%.o) +OBJ = $(OBJ_CPP:src/%.c=build/%.o) +OBJ_SIM_CPP = $(SRC:src/%.cpp=sim/build/%.o) +OBJ_SIM = $(OBJ_SIM_CPP:src/%.c=sim/build/%.o) + +CC= avr-g++ +CFLAGS= -Wall -mmcu=$(DEVICE) -Os -DF_CPU=$(CPU_FREQUENCY) -DBAUD_RATE=$(BAUDRATE) -DDOUBLE_SPEED -DNUM_LED_FLASHES=4 '-DMAX_TIME_COUNT=F_CPU>>4' -c +LFLAGS= -Wall -mmcu=$(DEVICE) -Os -DF_CPU=$(CPU_FREQUENCY) -Wl,--section-start=.text=$(START_ADDRESS) -Wl,-u,vfprintf -lprintf_flt -lm +#LFLAGS= -Wall -mmcu=$(DEVICE) -Os -DF_CPU=$(CPU_FREQUENCY) -Wl,--section-start=.text=$(START_ADDRESS) + +CFLAGS_SIM= -Wall -mmcu=$(DEVICE) -Og -DF_CPU=$(CPU_FREQUENCY) -g -DBAUD_RATE=$(BAUDRATE) -DDOUBLE_SPEED -DNUM_LED_FLASHES=4 '-DMAX_TIME_COUNT=F_CPU>>4' -c +LFLAGS_SIM= -Wall -mmcu=$(DEVICE) -Og -DF_CPU=$(CPU_FREQUENCY) -g -Wl,--section-start=.text=$(START_ADDRESS) -Wl,-u,vfprintf -lprintf_flt -lm +#LFLAGS_SIM= -Wall -mmcu=$(DEVICE) -Og -DF_CPU=$(CPU_FREQUENCY) -g -Wl,--section-start=.text=$(START_ADDRESS) + +# -------------------------------------------------------------------------------- +# make targets + +.PHONY: all +all: dist/$(NAME).elf sim/$(NAME).elf dist/$(NAME).s dist/$(NAME).hex dist/$(NAME).bin sim/$(NAME).s info + +.PHONY: info +info: + @echo + @avr-size --mcu=$(DEVICE) --format=avr dist/$(NAME).elf + +# -------------------------------------------------------------------------------- +# dependency make for hierarchical source file structure + +.depend: $(SRC) $(HDR) + $(CC) -mmcu=$(DEVICE) -MM $(SRC) | sed --regexp-extended 's/^(.*\.o)\: src\/(.*)(\.cpp|\.c) (.*)/build\/\2\.o\: src\/\2\3 \4/g' > .depend + +ifneq (clean,$(filter clean,$(MAKECMDGOALS))) +-include .depend +endif + +# -------------------------------------------------------------------------------- +# elf, hex and assembler file creation + +dist/$(NAME).elf: .depend $(OBJ) + $(CC) $(LFLAGS) -o $@ $(OBJ) + +dist/%.s: dist/%.elf + avr-objdump -d $< > $@ + +dist/%.hex: dist/%.elf + avr-objcopy -O ihex $(HEX_FLASH_FLAGS) $< $@ + +dist/%.bin: dist/%.elf + avr-objcopy -O binary $(HEX_FLASH_FLAGS) $< $@ + +# -------------------------------------------------------------------------------- +# check if the macros __DATE__ or __TIME__ are used in src/main.cpp or src/main.c + +DATE_USED= +ifneq ($(shell cat $(MAINSRC) | grep __DATE__),) + DATE_USED=true +endif +TIME_USED= +ifneq ($(shell cat $(MAINSRC) | grep __TIME__),) + TIME_USED=true +endif + +ifeq (true, $(filter true, $(DATE_USED) $(TIME_USED))) +build/main.o: $(MAIN_SRC) $(SRC) $(HDR) + @mkdir -p $(dir $@) + $(CC) $(CFLAGS) -o $@ $< +endif + +# -------------------------------------------------------------------------------- + +build/%.o: src/%.c + @mkdir -p $(dir $@) + $(CC) $(CFLAGS) -o $@ $< + +build/%.o: src/%.cpp + @mkdir -p $(dir $@) + $(CC) $(CFLAGS) -o $@ $< + +# -------------------------------------------------------------------------------- +# simulation/debugging with gdb or simuc + +sim/$(NAME).elf: .depend $(OBJ_SIM) + $(CC) $(LFLAGS_SIM) -o $@ $(OBJ_SIM) + @ln -sf $(NAME).elf sim/$(DEVICE).elf + +sim/build/%.o: src/%.c + @mkdir -p $(dir $@) + $(CC) $(CFLAGS_SIM) -o $@ $< + +sim/build/%.o: src/%.cpp + @mkdir -p $(dir $@) + $(CC) $(CFLAGS_SIM) -o $@ $< + +sim/%.s: sim/%.elf + avr-objdump -d $< > $@ + +ifeq (m16, $(AVRDUDE_DEVICE)) +simuc: sim/$(NAME).elf + simuc --board sure $< +endif + +ifeq (m328p, $(AVRDUDE_DEVICE)) +simuc: sim/$(NAME).elf + simuc --board arduino $< +endif + +ifeq (m644p, $(AVRDUDE_DEVICE)) +simuc: sim/$(NAME).elf + simuc --board nano-644 $< +endif + +ifeq (m1284p, $(AVRDUDE_DEVICE)) +simuc: sim/$(NAME).elf + simuc --board nano-1284 $< +endif + +gdb: sim/$(NAME).elf + avr-gdb $< + +# ------------------------------------------------------------- +# flash to target with arduino bootloader in bootloader-section + +.PHONY: flash +flash: dist/$(NAME).elf all + avrdude -c arduino -P /dev/ttyUSB0 -b $(BAUDRATE) -p $(AVRDUDE_DEVICE) -e -U flash:w:$< + +.PHONY: flash0 +flash0: dist/$(NAME).elf all + avrdude -c arduino -P /dev/ttyUSB0 -b $(BAUDRATE) -p $(AVRDUDE_DEVICE) -e -U flash:w:$< + +.PHONY: flash1 +flash1: dist/$(NAME).elf all + avrdude -c arduino -P /dev/ttyUSB1 -b $(BAUDRATE) -p $(AVRDUDE_DEVICE) -e -U flash:w:$< + +.PHONY: flash2 +flash2: dist/$(NAME).elf all + avrdude -c arduino -P /dev/ttyUSB2 -b $(BAUDRATE) -p $(AVRDUDE_DEVICE) -e -U flash:w:$< + +.PHONY: flash-read +flash-read: + avrdude -c arduino -P /dev/ttyUSB0 -b $(BAUDRATE) -p $(AVRDUDE_DEVICE) -U flash:r:/tmp/flash.bin + +.PHONY: flash-disassemble +flash-disassemble: flash-read + avr-objdump -b binary -D -m avr5 /tmp/flash.bin > /tmp/flash.s + less /tmp/flash.s + +.PHONY: flash-hexdump +flash-hexdump: flash-read + hexdump -C /tmp/flash.bin | less + +# ---------------------------------------------- +# flash to target with fischl programming device + +.PHONY: isp-flash-$(AVRDUDE_DEVICE) +isp-$(AVRDUDE_DEVICE): + avrdude -c usbasp -p $(AVRDUDE_DEVICE) -U lock:r:-:h + avrdude -c usbasp -p $(AVRDUDE_DEVICE) + +.PHONY: isp-flash +isp-flash: dist/$(NAME).elf all + avrdude -c usbasp -p $(AVRDUDE_DEVICE) -e -U flash:w:$< + +.PHONY: isp-flash-$(AVRDUDE_DEVICE) +isp-flash-$(AVRDUDE_DEVICE): dist/$(NAME).elf all + avrdude -c usbasp -p $(AVRDUDE_DEVICE) -e -U flash:w:$< + +.PHONY: isp-read-flash-$(AVRDUDE_DEVICE) +isp-read-flash-$(AVRDUDE_DEVICE): + avrdude -c usbasp -p m32$(AVRDUDE_DEVICE)8p -U flash:r:/tmp/flash-arduino-atmega328p__$(shell date +"%Y-%m-%d_%H%M%S") + +.PHONY: isp-fuse +isp-fuse: isp-fuse-$(AVRDUDE_DEVICE) + +.PHONY: isp-fuse-$(AVRDUDE_DEVICE) +ifeq (m16, $(AVRDUDE_DEVICE)) +isp-fuse-$(AVRDUDE_DEVICE): + avrdude -c usbasp -p $(AVRDUDE_DEVICE) -U lfuse:w:0x18:m -U hfuse:w:0xD8:m -U lock:w:0xEF:m +endif +ifeq (m328p, $(AVRDUDE_DEVICE)) +isp-fuse-$(AVRDUDE_DEVICE): + avrdude -c usbasp -p $(AVRDUDE_DEVICE) -U lfuse:w:0xEF:m -U hfuse:w:0xD8:m -U efuse:w:0xFD:m -U lock:w:0xEF:m +endif +ifeq (m644p, $(AVRDUDE_DEVICE)) +isp-fuse-$(AVRDUDE_DEVICE): + avrdude -c usbasp -p $(AVRDUDE_DEVICE) -U lfuse:w:0xEF:m -U hfuse:w:0xD8:m -U efuse:w:0xFD:m -U lock:w:0xEF:m +endif +ifeq (m1284p, $(AVRDUDE_DEVICE)) +isp-fuse-$(AVRDUDE_DEVICE): + avrdude -c usbasp -p $(AVRDUDE_DEVICE) -U lfuse:w:0xEF:m -U hfuse:w:0xD8:m -U efuse:w:0xFD:m -U lock:w:0xEF:m +endif + +# -------------------------------------------------------- +# picocom sends CR for ENTER -> convert cr (\r) to lf (\n) + +.PHONY: picocom +picocom: + picocom -b $(BAUDRATE) --omap crlf --raise-dtr /dev/ttyUSB0 + +.PHONY: picocom0 +picocom0: + picocom -b $(BAUDRATE) --omap crlf --raise-dtr /dev/ttyUSB0 + +.PHONY: picocom1 +picocom1: + picocom -b $(BAUDRATE) --omap crlf --raise-dtr /dev/ttyUSB1 + +.PHONY: picocom2 +picocom2: + picocom -b $(BAUDRATE) --omap crlf --raise-dtr /dev/ttyUSB2 + +# -------------------------------------------------------- + +.PHONY: help +help: + @echo + @echo "Possible targets are:" + @echo " clean" + @echo " all help info" + @echo " flash flash0 flash1 flash2 flash-read flash-disassemble flash-hexdump" + @echo " isp-$(AVRDUDE_DEVICE) isp-flash-$(AVRDUDE_DEVICE) isp-fuse-$(AVRDUDE_DEVICE)" + @echo " picocom picocom0 picocom1 picocom2" + @echo " gdb simuc" + @echo + +# -------------------------------------------------------- + +.PHONY: release +release: dist/$(NAME).elf sim/$(NAME).elf dist/$(NAME).hex dist/$(NAME).bin + ../create-release release $(word 1, $^) release/sim $(word 2, $^) + +# -------------------------------------------------------- + +.PHONY: clean +clean: + @rm -r dist + @rm -r build + @rm -r sim + @find . -type f -name ".depend" -exec rm {} \; + @echo "clean done" diff --git a/software/deye-sun-12k/nano-644/release/v2024-11-08_181803/nano-x-base_test-software_nano-m644p_12mhz.bin b/software/deye-sun-12k/nano-644/release/v2024-11-08_181803/nano-x-base_test-software_nano-m644p_12mhz.bin new file mode 100755 index 0000000000000000000000000000000000000000..0c7d86e337c9b4e32a6b2cb4e124e4f9c8702588 GIT binary patch literal 38296 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 literal 0 HcmV?d00001 diff --git a/software/deye-sun-12k/nano-644/release/v2024-11-08_181803/nano-x-base_test-software_nano-m644p_12mhz.elf b/software/deye-sun-12k/nano-644/release/v2024-11-08_181803/nano-x-base_test-software_nano-m644p_12mhz.elf new file mode 100755 index 0000000000000000000000000000000000000000..7d3360736840c60b1a47e53196763cce24f7b525 GIT binary patch literal 74852 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

Wzq$}rh&T?!4y*$ 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 literal 0 HcmV?d00001 diff --git a/software/deye-sun-12k/nano-644/release/v2024-11-08_181803/nano-x-base_test-software_nano-m644p_12mhz.hex b/software/deye-sun-12k/nano-644/release/v2024-11-08_181803/nano-x-base_test-software_nano-m644p_12mhz.hex new file mode 100644 index 0000000..e37ec65 --- /dev/null +++ b/software/deye-sun-12k/nano-644/release/v2024-11-08_181803/nano-x-base_test-software_nano-m644p_12mhz.hex @@ -0,0 +1,2396 @@ +:100000000C94B1070C94D9070C94D9070C94D90718 +:100010000C94D9070C94D9070C94D9070C94D907E0 +:100020000C94D9070C94C90B0C94D9070C94D907DC +:100030000C94D9070C94D9070C94D9070C94D907C0 +:100040000C94D9070C94D9070C94D9070C94D907B0 +:100050000C94190B0C94D9070C94D9070C94D9075C +:100060000C94D9070C94D9070C948D0B0C94D907D8 +:100070000C944C0B0C94D9070C94D90707634236A7 +:10008000B79BD8A71A39685618AEBAAB558C1D3C29 +:10009000B7CC5763BD6DEDFD753EF6177231BF00ED +:1000A0000000803F08000000BE922449123EABAA27 +:1000B000AA2ABECDCCCC4C3E00000080BEABAAAA82 +:1000C000AA3E00000000BF000000803F00000000CA +:1000D00000084178D3BB4387D1133D190E3CC3BD03 +:1000E0004282AD2B3E68EC8276BED98FE1A93E4CB0 +:1000F00080EFFFBE01C4FF7F3F00000000006E6183 +:100100006E00696E660000407A10F35A00A0724ECD +:1001100018090010A5D4E80000E87648170000E4AC +:100120000B54020000CA9A3B000000E1F5050000F4 +:1001300080969800000040420F000000A086010059 +:100140000000102700000000E80300000000640029 +:10015000000000000A000000000001000000000094 +:100160002C76D888DC674F0823DFC1DFAE59E1B1B8 +:10017000B796E5E3E453C63AE651997696E8E6C2C7 +:100180008426EB898C9B62ED407C6FFCEFBC9C9FCE +:1001900040F2BAA56FA5F490055A2AF75C936B6CF0 +:1001A000F9676DC11BFCE0E40D47FEF520E6B500E4 +:1001B000D0ED902E0300943577050080841E080052 +:1001C00000204E0A000000C80C333333330F986E02 +:1001D00012831141EF8D2114893BE65516CFFEE6BF +:1001E000DB18D1844B381BF77C1D901DA4BBE42485 +:1001F000203284725E228100C9F124ECA1E53D2702 +:100200006364696E6F70737578585B000A25346497 +:100210003A20005D3A20000A0A5B000A53656C65CB +:10022000637420756E69743A2000253378202E2E71 +:100230002E2000417661696C61626C6520756E6983 +:1002400074733A0A0A004E6F204E616E6F2D582D5E +:1002500042617365206465746563746564004E6112 +:100260006E6F2D582D426173653A20255300202F63 +:100270002000202F20000A0A4861726477617265AD +:1002800020255320646574656374656420284144A7 +:100290004337483D30782530325829000A496E7678 +:1002A000616C6964204E616E6F2D582D42617365DB +:1002B0002048617264776172652D56657273696F4B +:1002C0006E3A204144433748203D20256420284190 +:1002D000546D656761363434502C20332E335629E3 +:1002E0000A00563F3F005632610056316100446FAC +:1002F0006E65004552524F52000A000A3D3D3D3D99 +:100300003D3D3D3D3D3D3D3D3D3D3D3D3D3D3D3D1D +:100310003D3D3D3D3D3D3D3D3D3D3D3D3D3D3D3D0D +:100320003D0A200041546D65676136343450003118 +:10033000383A31383A3033004E6F76202038203248 +:10034000303234000A505441424C452025643A2052 +:100350003078253032780020213D20307825303229 +:1003600078000A00205B5741524E3A20627566665B +:10037000657220746F20736D616C6C5D2000204F7E +:100380004B5B7265636569766520256420627974CC +:1003900065735D2000204552524F525B7265636564 +:1003A0006976652025642062797465732C206578F0 +:1003B000706563742025642062797465735D200024 +:1003C0005D2000203078253032580020537461744D +:1003D00065735B004552525D005D0020307825302A +:1003E0003278000A205B72656164526567697374D4 +:1003F000657273283078253032782C202E2E2E2CE2 +:10040000202564295D202D3E20004552525D00309C +:1004100078253032782C7374617475733D3078258B +:100420003032785D000A205B7265616452656769ED +:10043000737465722830782530327829202D3E205B +:10044000004552525D007374617475733D307825B8 +:100450003032785D00202D3E20002030782530326B +:1004600078000A205B7772697465526567697374F6 +:1004700065727328307825303278293A2000455249 +:10048000525D007374617475733D30782530327835 +:100490005D000A205B7772697465526567697374E1 +:1004A0006572283078253032782C2030782530322B +:1004B0007829202D3E20004F4B006661696C730047 +:1004C0004F4B2C20696E69742050415441424C4579 +:1004D000202E2E2E20006661696C73004F4B2C205D +:1004E000696E6974207265676973746572202E2E57 +:1004F0002E20006661696C7300207265736574203C +:10050000434331313031202E2E2E200043432D31F4 +:100510003130312D3F0043432D313130312D546581 +:1005200073740043432D313130312D526563656959 +:1005300076650043432D313130312D53656E6400B3 +:10054000646F6E65202873746174653D3078253062 +:1005500032782900202E2E2E2000202530325800FF +:100560002564206461746120627974657320284871 +:100570004558293A20004552524F522C20004F4BEB +:100580002C200020525353493D25642C204C5149C6 +:100590003D25642C204352432000202D3E20004561 +:1005A000320063616E63656C6C6564004532004FB8 +:1005B0004B2C2072656365697665202E2E2E2000F7 +:1005C0003F202849444C45290063616E63656C6C8B +:1005D0006564004531000A205B253034785D203D9C +:1005E0003E207374617274202E2E2E20004F4B001B +:1005F0004531005D202D3E2000202530325800205E +:100600002D2D3E2073656E6420256420627974650B +:100610007320284845583A20253032580030782534 +:10062000303278004531000A205B253034785D3A5D +:100630002073746174653D006661696C73002850B5 +:10064000415441424C45203D2030782530325829D4 +:100650000064426D20000A2073776974636820701B +:100660006F77657220746F200020757365202B20D2 +:10067000616E64202D20666F7220706F7765722026 +:100680006368616E676520286F74686572206B65AA +:1006900079202D3E206261636B20746F2064656653 +:1006A00061756C742900292E06477A0E1404040023 +:1006B000000600216276CAF81622F84007301816A4 +:1006C0006C434991876BFB5610E92A001F41005982 +:1006D0007F3F813509C6392E3600C031310000C553 +:1006E00031300000CD37000000863500000050306A +:1006F000000000372D360000262D3130001D2D3131 +:100700003500172D323000032D333000253564209D +:10071000283078253032782920000D20203D3E20D9 +:10072000456E636F64657220287075736820746FFE +:1007300020636C656172293A2000456E636F6465C1 +:100740007200656E64000A2049324320534C4156C2 +:10075000453A2066726F6D206D61737465723A2040 +:10076000307825303278202D3E20746F206D6173F3 +:100770007465723A20307825303278004531002097 +:100780002D3E204552524F520030782530327800AD +:100790002C202066726F6D20736C6176653A2000A4 +:1007A000202D3E204552524F52000A204932432DFF +:1007B0004D41535445523A20746F20736C617665F5 +:1007C0003A20307825303278004531004533002911 +:1007D0000062616400706F6F720066616972006729 +:1007E0006F6F6400657863656C6C656E74003F00C4 +:1007F0002C2065636F323D25642800292C207476F7 +:100800006F633D2564707062003F00756E686561BE +:100810006C74687900706F6F72006D6F64657261DF +:10082000746500676F6F6400657863656C6C656EF6 +:100830007400206171693D25642800453200252E31 +:100840003166C2B0432F252E31662525293A200076 +:100850004531290020207C20454E53313630202858 +:10086000002C20483D20252E32662525002C2054C2 +:100870003D20252E3266C2B043000A203D3E204274 +:100880004D3238303A20503D20252E336662617259 +:10089000004F4B004534004533004532004F4B2C90 +:1008A00020454E53313630202E2E2E20004531006B +:1008B00020424D323830202E2E2E20004932432D3A +:1008C000536C617665004932432D4D6173746572D6 +:1008D000004932432D537061726B66756E20456E10 +:1008E000762D436F6D626F00656E64000A20203DB7 +:1008F0003E207265636569766520427974653A20A9 +:10090000307825303278000A20203D3E2073656E15 +:10091000642042797465203078253032780049456A +:10092000454534383500656E64004552524F5228B3 +:10093000256429004F4B004C434420004552524F40 +:100940005220256429004F4B2900696E6974204CA0 +:1009500043442028004C63640054657374204C4564 +:100960004420442564004C656400656E6400202DBD +:100970003E2025342E3866560A0020307825303245 +:1009800078002020003F000A202020202020205234 +:100990007844313A0020307825303278000A203D02 +:1009A0003E2053656E64696E673A004D6F646275F0 +:1009B000733A206C657365205370616E6E756E6757 +:1009C00020766F6E2045617374726F6E2053444DB4 +:1009D0002D323330202845696E70686173656E7AF8 +:1009E000C3A4686C65722900202E2E2E20707265BB +:1009F0007373206B657920746F2070726F63656507 +:100A000064006E6F20627974652072656365697633 +:100A100065640030782530327820726563656976C8 +:100A20006564002044453D25752C206E52453D25CA +:100A3000752073656E64203078253032782E2E2E26 +:100A40002000696E6974004D6F6462757300202028 +:100A50006E6F20726F746174696F6E2028543D252B +:100A60003034782920200020206E3D202535642058 +:100A7000552F6D696E2028543D253034782900208B +:100A800053454E534F523D25642000206E4641553C +:100A90004C543D25640020414443323D25336400DD +:100AA000202050574D2F4F4330413D253364000DDA +:100AB00020203D3E20414443303D25336400454ED7 +:100AC0003D3100454E3D30000D205357333D2564E8 +:100AD0002D3E000A004D6F746F72000A20204261A3 +:100AE0006E6B30202D204750422564203D20300081 +:100AF0000A202042616E6B30202D20475042256431 +:100B0000203D2031000A202042616E6B30202D20D4 +:100B10004750412564203D2030000A202042616E6C +:100B20006B30202D204750412564203D2031000AA4 +:100B3000203D3E207374617274202E2E2E002900F9 +:100B4000204F4B00204A5033392E322F33206A7504 +:100B50006D706572656420286C656674293F00209D +:100B60004552524F5200202872656164203078252A +:100B7000303278202D3E2030782530327800506F8A +:100B8000727445787000253464202830782530331D +:100B90007829000D20203D3E204D656173757265FA +:100BA00020414443303A20000A00506F746900200D +:100BB00025332E3166203D3E205357393A36203DAD +:100BC000202564202564252064202564202000251C +:100BD0003464202830782530337829000D20203DDA +:100BE0003E204D65617375726520414443323A2061 +:100BF000000A0052325200416C6C00426C75650074 +:100C0000477265656E005265640052676200202578 +:100C1000303278000A20257020257020257020258C +:100C200070202570202570202570202D3E207772A1 +:100C30006974653A2000706F776572206F66662070 +:100C40006661696C732C204920616D207374696C36 +:100C50006C20616C697665203A2D29202E2E2E207D +:100C600070726F63656564000A20706F776572202B +:100C70006F6666206E6F77202E2E2E000D2070720C +:100C80006573732045534320746F2061626F7274E3 +:100C90002C20706F776572206F666620696E202544 +:100CA000647320287072657373206B657920746F8C +:100CB00020736B69702074696D65722920000A00C9 +:100CC0000A20706F776572206F6E202E2E2E0066C0 +:100CD00061696C73004F4B000A2054696D65722086 +:100CE0006F6666202E2E2E20000A2054696D6572D4 +:100CF0002073657420746F202564732028313A2591 +:100D000030325829202E2E2E20000A207365743A86 +:100D100020253034642D253032642D253032642076 +:100D2000253032643A253032643A2530326400206E +:100D30002D2054696D65723D307825303278002C55 +:100D400020256420253034642D253032642D253053 +:100D5000326420253032643A253032643A2530320C +:100D6000640020202D2D3E20003F3F002025303202 +:100D7000580020000A203D3E2072656164207265A3 +:100D800067697374657220302D3135202868657865 +:100D9000293A000A202020732F53202E2E2073651D +:100DA000636F6E6420282B2F2D290A000A20202033 +:100DB0006E2F4E202E2E206D696E75746520282BA7 +:100DC0002F2D29000A202020682F48202E2E206851 +:100DD0006F757220282B2F2D29000A202020642FC8 +:100DE00044202E2E2064617920282B2F2D29000AE3 +:100DF0002020206D2F4D202E2E206D6F6E746820C8 +:100E0000282B2F2D29000A202020792F59202E2E23 +:100E1000207965617220282B2F2D29000A2020209F +:100E2000772F57202E2E207765656B6461792028F7 +:100E30002B2F2D290A000A20202063202E2E2E2E53 +:100E400020696E697420636C6F636B000A20202038 +:100E500070202E2E2E2E20706F776572206F6E2FD1 +:100E60006F666620285043372D3E513129000A20F5 +:100E7000202074202E2E2E2E2074696D6572206F16 +:100E80006E2F6F6666000A2070726573733A000AEF +:100E9000203D3E20636F6E6669672038353633200B +:100EA0002E2E2E20005254432D3835363300536FEA +:100EB000004D6F004469004D6900446F00467200A8 +:100EC0005361000043686172202564202D20257342 +:100ED000202D3E2025303278004C312F4C32204FCF +:100EE0004E004C312F4C32204F4E004F4646004FA3 +:100EF0004E00536567370052656C6561736520531A +:100F00005725642000507265737320535725640081 +:100F10005377697463680048656C6C6F2055415263 +:100F200054312C204543484F2D4D6F64757320611B +:100F300063746976650A000A203D3E2073656E641D +:100F400020746578742076696120554152543120AF +:100F50006E6F772E2E2E0055617274310000893D20 +:100F60004C3E11241FBECFEFD0E1DEBFCDBF12E05B +:100F7000A0E0B1E0E2E4F4E902C005900D92A635EC +:100F8000B107D9F724E0A6E5B2E001C01D92A83E62 +:100F9000B207E1F717E0C1EBD7E004C02197FE01EB +:100FA0000E94A940CF3AD107C9F70E94F23B0C94A6 +:100FB0001F4A0C940000FC01108211820895FC016C +:100FC0009181992311F0915091830895FC016083E0 +:100FD00084E68093B80084E08093BC0081E00895AB +:100FE00084E08093BC001092B80008954150FB014A +:100FF00094E824EC4F3FD1F0442319F02093BC0037 +:1010000002C09093BC008091BC0087FFFCCF809110 +:10101000B900887F442319F0803519F009C088355C +:1010200039F48091BB0081934150E4CF81E0089571 +:1010300080E00895DC01FB0184E835E0442389F079 +:10104000222311F0949101C090819093BB00809372 +:10105000BC0011963C9311979091BC0097FFFCCF78 +:1010600002C081E008959091B90041503196987F77 +:10107000983221F380E00895FC0184EA8093BC005B +:1010800085E081838091BC0087FFFCCF9091B900FF +:10109000987F983011F09031B1F49081990F962B90 +:1010A0009093BB0084E88093BC0085E081838091AD +:1010B000BC0087FFFCCF9091B900987F611105C0FB +:1010C00081E0983129F080E0089581E09034D9F7EB +:1010D00008950F931F93CF93DF93D62F8A01C22FCA +:1010E00060E00E943C08811102C080E016C0F80157 +:1010F00024E8D093BB002093BC009091BC0097FFE4 +:10110000FCCF9091B900987F983279F7D1919FEFF9 +:101110009C0FCC2311F0C92FECCFDF91CF911F9101 +:101120000F91089584E98093BC008091BC0084FDF8 +:10113000FCCF81E00895FF920F931F93CF93DF932D +:10114000EC018B01F42E61E00E943C08882379F0C9 +:101150004F2DB801CE010E94F607882341F0CE0141 +:10116000DF91CF911F910F91FF900C94920880E036 +:10117000DF91CF911F910F91FF900895FF920F93F0 +:101180001F93CF93DF93EC018B01F42E60E00E945C +:101190003C08882381F020E04F2DB801CE010E9449 +:1011A0001A08882341F0CE01DF91CF911F910F9152 +:1011B000FF900C94920880E0DF91CF911F910F91E6 +:1011C000FF900895FF920F931F93CF93DF93EC014D +:1011D0008B01F42E60E00E943C08882381F021E01E +:1011E0004F2DB801CE010E941A08882341F0CE018C +:1011F000DF91CF911F910F91FF900C94920880E0A6 +:10120000DF91CF911F910F91FF900895CF92DF92C0 +:10121000EF92FF920F931F93CF93DF93EC016B013B +:10122000142F790160E00E943C08882301F120E03E +:10123000412FB601CE010E941A088823C1F061E057 +:10124000CE010E943C08882391F0402FB701CE01C7 +:101250000E94F607882359F0CE01DF91CF911F91AC +:101260000F91FF90EF90DF90CF900C94920880E068 +:10127000DF91CF911F910F91FF90EF90DF90CF9072 +:101280000895FC011082118212821386148608953B +:10129000FC019081992311F091509083089567FD8E +:1012A0000CC0660F642B6093BA0084E68093B8008C +:1012B00085E48093BC0081E0089580E0089584E097 +:1012C0008093BC001092B8000895FB012FB7F894EA +:1012D000918181E0890F8183DB01A90FB11D1296F5 +:1012E0004C93883008F0118281819081891305C068 +:1012F0008F5F8083883008F010822FBF0895462FBB +:10130000BC01655F7F4F0C946509FB019FB7F894A2 +:1013100080812181821304C09FBF8FEF9FEF0895CA +:1013200021E0280F2083DB01A80FB11D12968C91BC +:10133000283018F49FBF90E008951082FBCFBC01C5 +:101340006F5F7F4F0C9485092091B900287F20386A +:1013500019F0283A49F013C04091BB00BC016F5FFF +:101360007F4F0E94650909C0BC01655F7F4F0E94E5 +:10137000850997FD80E08093BB0085E801C085EC7E +:101380008093BC000895CF93DF9390915F04809188 +:101390005E049817D1F3E0915F0481E08E0F809393 +:1013A0005F04F0E0E05AFB4FC081CD3009F4CAE0A1 +:1013B000D0E06091E4047091E504CE010E942B46D8 +:1013C000CE01DF91CF9108950F931F93CF93C82F34 +:1013D0008B018A3019F48DE00E94E4098091E404C5 +:1013E0009091E5040817190731F48091C00085FF3A +:1013F000FCCFC093C60080E090E0CF911F910F9189 +:101400000895089580E090E008950895089508955E +:101410000895089508952FB7F8944091900450913D +:101420009104452B59F4429A80383FEF930710F40A +:1014300080589F4F90939104809390042FBF0895FC +:101440002FB7F8944091920450919304452B59F48E +:10145000449A80383FEF930710F480589F4F909341 +:101460009304809392042FBF0895813041F082301D +:1014700019F486EE92E0089582EE92E008958AEEE5 +:1014800092E00895CF9387E680937C0087E880936D +:101490007A0080917A00806480937A0080917A004B +:1014A00086FDFCCF1092880480917900843F10F073 +:1014B00081E005C080917900803E18F082E0809341 +:1014C0008804809188049FEF980F923088F0809173 +:1014D00079001F928F938CE992E09F938F930E94E3 +:1014E0006746109288040F900F900F900F9014C0D1 +:1014F000C09179000E94350A1F92CF939F938F93DA +:1015000086E792E09F938F930E9467460F900F901B +:101510000F900F900F900F9010927C0010927A0015 +:1015200080918804CF910895F89460938C0470930F +:101530008D0480938E0490938F04789408950E9474 +:10154000940AF89480918C0490918D04A0918E045B +:10155000B0918F047894892B8A2B8B2B31F080915A +:10156000000190910101019661F380910001909139 +:1015700001010895CF93DF93CFEFDFEFD093010107 +:10158000C09300010E949F0AD0930101C093000103 +:10159000DF91CF910895CF93DF93CDB7DEB7289732 +:1015A0000FB6F894DEBF0FBECDBF7091800460917E +:1015B00081045091820440918304309184042091ED +:1015C0008504909186048091870479836A835B8384 +:1015D0004C833D832E839F8388878FB7F894E09157 +:1015E0008004709181046091820450918304409141 +:1015F0008404309185042091860490918704E983C6 +:101600007A836B835C834D833E832F8398878FBF60 +:1016100029813A814B815C816D817E818F819885A2 +:1016200028960FB6F894DEBF0FBECDBFDF91CF91E5 +:1016300008951F920F920FB60F9211242F933F938C +:101640008F939F93EF93FF938091C600282F30E0F4 +:101650003093010120930001E0915E0491E09E0F20 +:1016600090935E04F0E0E05AFB4F808390915E041B +:1016700080915F04981305C080915F048F5F809311 +:101680005F04FF91EF919F918F913F912F910F9068 +:101690000FBE0F901F9018951F920F920FB60F92CA +:1016A00011242F933F934F935F936F937F938F9367 +:1016B0009F93AF93BF93CF93EF93FF93C091CE00CF +:1016C00080912D04882329F06C2F8BE294E00E94F6 +:1016D000142C80914104882329F06C2F8FE394E02F +:1016E0000E94473880912804882329F06C2F86E2D5 +:1016F00094E00E943C27FF91EF91CF91BF91AF9171 +:101700009F918F917F916F915F914F913F912F9119 +:101710000F900FBE0F901F9018951F920F920FB64B +:101720000F9211242F933F934F935F936F937F9367 +:101730008F939F93AF93BF93EF93FF938091940305 +:10174000882319F084E093E00DC08091030388237F +:1017500019F083E792E006C080912504882329F0E0 +:1017600085E993E00E94852603C080E88093BC0051 +:10177000FF91EF91BF91AF919F918F917F916F9169 +:101780005F914F913F912F910F900FBE0F901F903F +:1017900018951F920F920FB60F9211242F933F931B +:1017A0004F935F936F937F938F939F93AF93BF9369 +:1017B000EF93FF9380915104882321F08FE494E00C +:1017C0000E94FD2280914A04882321F088E494E05D +:1017D0000E946B2D80918B048F5F8A3018F4809368 +:1017E0008B0494C010928B0480918C0490918D0492 +:1017F000A0918E04B0918F04892B8A2B8B2B99F0AA +:1018000080918C0490918D04A0918E04B0918F04EE +:101810000197A109B10980938C0490938D04A09342 +:101820008E04B0938F042091800430918104409104 +:1018300082045091830460918404709185048091A6 +:10184000860490918704A1E00E9466402093800462 +:101850003093810440938204509383046093840402 +:101860007093850480938604909387048DE793E0BA +:101870000E94DF078FE793E00E9448098CEE92E018 +:101880000E94DF078EEE92E00E9448098EE094E00D +:101890000E94DF0780E194E00E94480980916A027B +:1018A000882329F080916A02815080936A02809196 +:1018B0005F02882329F080915F02815080935F024C +:1018C0008091900490919104892B69F0809190040B +:1018D00090919104019790939104809390048038A3 +:1018E000910508F442988091920490919304892B79 +:1018F00069F08091920490919304019790939304DE +:10190000809392048038910508F44498809189046A +:1019100090918A04019690938A0480938904883870 +:10192000934140F098B188E0892788B910928A04E1 +:1019300010928904FF91EF91BF91AF919F918F9188 +:101940007F916F915F914F913F912F910F900FBEBB +:101950000F901F901895CF93DF93EC011982188296 +:101960008091D50481110EC085ED94E00E94020A99 +:10197000892B41F083ED94E00E94DB0785ED94E034 +:101980000E94050A198A1A8A1B8A1C8A88E994E02F +:101990009B838A8386E994E09D838C8384E994E029 +:1019A0009F838E8383ED94E099838883DF91CF9129 +:1019B0000895CF93DF9300D0CDB7DEB74A83FC0103 +:1019C00080819181009739F0698342E0BE016F5FA9 +:1019D0007F4F0E94BE080F900F90DF91CF91089526 +:1019E000CF92EF920F93CF93DF93EC016295660F46 +:1019F000660F607C47702770822F880F880F880FD2 +:101A0000262F242B282B2FAB0295000F007E88ADAC +:101A10008F71082B08AF8E2D8770880F880F880F65 +:101A20009EA9292F207C9C2D9770E22EE82AE92A76 +:101A3000E894E7F8EEAA40E064EFCE010E94D90CEA +:101A400048AD42954695477062EFCE010E94D90C91 +:101A50009EA9492F477060E2469F90011124892F6B +:101A6000869586958695877064E0869FA0011124EF +:101A7000422B532B97FB992790F9492B65EFCE0109 +:101A80000E94D90C9FA9492F477080E2489F90017E +:101A90001124892F869586958695877064E0869FA8 +:101AA000A0011124422B532B9295969596959370F5 +:101AB000492B64EFCE01DF91CF910F91EF90CF9042 +:101AC0000C94D90C0F93CF93DF931F92CDB7DEB751 +:101AD000FC0180819181009749F0698301E09E01BA +:101AE0002F5F3F4F41E0B9010E94060989810F90A5 +:101AF000DF91CF910F9108950F93CF93DF9300D093 +:101B0000CDB7DEB7FC0180819181009749F06983F0 +:101B100002E09E012F5F3F4F41E0B9010E9406099C +:101B200089819A819827892798270F900F90DF91B4 +:101B3000CF910F9108950E947C0D982789279827AF +:101B400008950C947C0D0C949B0D0F93CF93DF9311 +:101B500000D01F92CDB7DEB7FC0180819181009744 +:101B600049F0698303E09E012F5F3F4F41E0B901D7 +:101B70000E94060929816A81862F90E0A0E0B0E0EA +:101B8000BA2FA92F982F8827A22B2B81BC01CD011A +:101B9000622B0F900F900F90DF91CF910F910895CE +:101BA0006F927F928F929F92AF92BF92CF92DF926D +:101BB000EF92FF920F931F93CF93DF93EC019FA9B6 +:101BC000892F807C803409F043C0492F477060E240 +:101BD000469F90011124892F86958695869587705A +:101BE000E4E08E9FA0011124422B532B92959695F1 +:101BF00096959370492B64EFCE010E94D90C0E94F8 +:101C0000CB0A922E832E742E652E63EFCE010E9496 +:101C1000620D83FF1FC00E94CB0AA92CB82CC72CD1 +:101C2000D62CE12CF12C00E010E00E947240203D07 +:101C3000374041055105610571058105910509F0A0 +:101C400038F487EB9BE00197F1F700C00000DDCF8F +:101C500080E001C081E0DF91CF911F910F91FF9053 +:101C6000EF90DF90CF90BF90AF909F908F907F903C +:101C70006F9008951F93CF93DF93EC0168E80E9463 +:101C80009B0D9E8B8D8B6AE8CE010E949B0D988FD9 +:101C90008F8B6CE8CE010E949B0D9A8F898F6EE826 +:101CA000CE010E949B0D9C8F8B8F60E9CE010E941C +:101CB0009B0D9E8F8D8F62E9CE010E949B0D98A394 +:101CC0008F8F64E9CE010E949B0D9AA389A366E9D8 +:101CD000CE010E949B0D9CA38BA368E9CE010E94BC +:101CE0009B0D9EA38DA36AE9CE010E949B0D98A730 +:101CF0008FA36CE9CE010E949B0D9AA789A76EE97C +:101D0000CE010E949B0D9CA78BA761EACE010E9489 +:101D1000620D8DA761EECE010E949B0D9FA78EA73D +:101D200063EECE010E94620D88AB64EECE010E948C +:101D3000620D182F65EECE010E94620D90E119022E +:101D4000900111248F70282B3AAB29AB66EECE019F +:101D50000E94620D182F65EECE010E94620D90E187 +:101D600019029001112490E044E0959587954A95D9 +:101D7000E1F7822B932B9CAB8BAB67EECE010E94DD +:101D8000620D8DABDF91CF911F91089563EF0E949B +:101D9000620D81700895CF92DF92EF92FF920F93C0 +:101DA0001F93CF93DF93EC0160ED0E94620D90E0F2 +:101DB000A0E0B0E089879A87AB87BC878036910521 +:101DC000A105B10569F546EB60EECE010E94D90C84 +:101DD0008FE295E70197F1F700C00000CE010E9465 +:101DE000C60E8111F5CFCE010E943A0EC12CD12C26 +:101DF000E12CF12C05E010E025E030E045E050E07A +:101E000063E070E0CE010E94F00C9FE729EA83E0D6 +:101E1000915020408040E1F700C0000081E001C007 +:101E200080E0DF91CF911F910F91FF90EF90DF90B5 +:101E3000CF900895CF93DF93EC01888199810E9420 +:101E4000E607882329F0CE01DF91CF910C94CB0EC9 +:101E500080E0DF91CF9108954F925F926F927F92D1 +:101E60008F929F92AF92BF92CF92DF92EF92FF92AA +:101E7000CF93DF93EC016AEF0E94A50D6115710508 +:101E800020E88207910509F485C06B017C0184E09C +:101E9000F594E794D794C7948A95D1F74D885E88D6 +:101EA000C701B60128E030E040E050E00E94C43EA7 +:101EB000612C712CD301C201880F991FAA1FBB1F6F +:101EC000281B390B4A0B5B0BAF89B88D0E94EE3E85 +:101ED0004B015C01C701B60120E130E040E050E079 +:101EE0000E94C43ECA01B90164197509860997099F +:101EF0009B01AC010E94863E9B01AC017CE05595A4 +:101F00004795379527957A95D1F7A98DBA8D0E9477 +:101F1000EE3E2B013C01C501B40120E038E040E079 +:101F200050E00E94C43E69017A01C301B20120E081 +:101F300030E440E050E00E94C43EBA01A9014C0DDB +:101F40005D1D6E1D7F1D89899A89AB89BC899A01A7 +:101F5000AB01280F391F4A1F5B1F2D873E874F8714 +:101F6000588BA5E0B0E00E94E33E60587F4F8F4F52 +:101F70009F4F20E031E040E050E00E94C43ECA01A3 +:101F8000B9010E94DB4120E030E048EC52E40E94BD +:101F90002B4104C060E070E080EC9FE7DF91CF91BF +:101FA000FF90EF90DF90CF90BF90AF909F908F9079 +:101FB0007F906F905F904F9008952F923F924F9235 +:101FC0005F926F927F928F929F92AF92BF92CF92C9 +:101FD000DF92EF92FF920F931F93CF93DF93CDB7D2 +:101FE000DEB76D970FB6F894DEBF0FBECDBF1C01F4 +:101FF0000E942C0F67EFC1010E94A50D6F87788B9F +:10200000898B9A8B611571058048910509F49AC2F4 +:10201000F10185859685A785B0896C017D01FF0C4E +:10202000CC08DC2C76019C01AD016C2D7C2D8C2D17 +:102030009C2D345F41405109610971098109910961 +:1020400029873A874B875C873B014C0159016A011C +:102050007B018C010E94F63E422E3D874E875B8BB2 +:102060006C8B7D8B8E8B9B8FD1015E963C915E97A6 +:102070005D962C91932F990F990B492F592F692F0A +:10208000792F892F0E94F63E0CE00E9426402C8F6B +:102090003D8F49835F8B688F798F582E9A8FF1011E +:1020A00030A1278D932F990F990BA42CBD84CE843A +:1020B000DB88EC88FD880E891B8D492F592F692FED +:1020C000792F892F0E94F63EF22E032F142FB52F61 +:1020D000F62FE72FD82EE92EA0E00E947B4084F453 +:1020E00021503F4F4F4F5F4F6F4F7F4F8F4F9F4F4D +:1020F000F22E032F142FB52FF62FE72FD82EE92E0F +:102100002F2D302F412F5B2F6F2F7E2F8D2D9E2D4A +:1021100008E00E943F40AC8CBD8CC980DF88E88C11 +:10212000F98C052D1A8D0E945D4070588F4F9F4F7E +:10213000D1015B96ED91FC915C975F01C12CD12C94 +:10214000E12CF12C8701E983BA82CB82DC82ED821B +:10215000FE820F831887C12CD12CE12CF12C00E0DA +:1021600010E00E94F63E122F3F8B4983B52FF62FC9 +:10217000E72FF82E092FA0E00E947B4084F4215025 +:102180003109410951096E4F7F4F8F4F9F4F122FD9 +:102190003F8B4983B52FF62FE72FF82E092F212FDC +:1021A0003F8949815B2F6F2F7E2F8F2D902F01E26A +:1021B0000E943F402D8F532E49835F8B688F798F0C +:1021C0008A8F9C8FA0E00E947B4009F4C0C1D1019E +:1021D00096963C91969795962C91932F990F990BE3 +:1021E000A42CBD84CE84DB88EC88FD880E891B8DF1 +:1021F000492F592F692F792F892F0E94F63E2D875D +:102200003E87442E5B8B6C8B7D8B8E8B9B8FF1017D +:1022100034A123A1932F990F990BA984BA84CB845D +:10222000DC8473018401492F592F692F792F892F5D +:102230000E94F63E01E10E942640AD84BE84C42C7B +:10224000DB88EC88FD880E891B8D0E945D4059015A +:102250006A017B01482E192F4F85588969897A892F +:1022600034E075956795579547953A95D1F780E095 +:1022700090E0A0E1B0E0841B950BA60BB70B3C01EE +:102280004D01990C6608762C43019C01AD01662D29 +:10229000762D862D962D0FE10E942640722E832EDC +:1022A000942E652EB62FA72F8A879B87F10132A126 +:1022B00021A1932F990F990B492F592F692F792F0E +:1022C000892F03E20E942640042D0E945D4059019F +:1022D0006A017B018C01272D382D492D562D6B2F3E +:1022E0007A2F8A859B850E947240E5E3AE2EFCE042 +:1022F000BF2EC12CD12CE12CF12C00E010E00E946B +:10230000F63EAD8CB52CC980DF88E88CF98C0A8D3F +:102310001C8D0E94513F2C8B3B8B2A013B014C01B1 +:10232000F22E032F142FB52DF62FE72DD82EE92CE2 +:10233000A0E00E947B4084F42150304E4F4F5F4F0D +:102340006F4F7F4F8F4F9F4FF22E032F142FB52FBC +:10235000F62FE72FD82EE92E2F2D302F412F5B2F70 +:102360006F2F7E2F8D2D9E2D0DE00E943F402987DF +:102370003A874B875C876F8779838D879E87D101EA +:102380009C963C919C979B962C91932F990F990B1F +:10239000A984BA846A017B01082F1E85492F592F11 +:1023A000692F792F892F0E94F63E0E94F63EF22E69 +:1023B000032F142FB52FF62FE72FD82EE92EA0E0EC +:1023C0000E947B4084F42150310941095E4F6F4FD8 +:1023D0007F4F8F4F9F4FF22E032F142FB52FF62FC5 +:1023E000E72FD82EE92E2F2D302F412F5B2F6F2F67 +:1023F0007E2F8D2D9E2D09E10E943F40AC88BB8829 +:102400006201730184010E945D4029873A874B87EE +:102410005C876F8779838D879E87F10132A521A51F +:10242000932F990F990B492F592F692F792F892FA6 +:102430000E94F63E122F3C8B4B8BB52FF62FE72FC9 +:10244000F82E092FA0E00E947B4084F4215031092E +:10245000484F5F4F6F4F7F4F8F4F9F4F122F3C8BD7 +:102460004B8BB52FF62FE72FF82E092F212F3C8904 +:102470004B895B2F6F2F7E2F8F2D902F03E10E94B2 +:102480003F4059016A017B018C0129853A854B85C2 +:102490005C856F8579818D859E850E945D40122FB8 +:1024A00039874983B52FF62FE72FF82E092FA0E0A3 +:1024B0000E947B4084F421503F4F4F4F5F4F6F4F3E +:1024C0007F4F8F4F9F4F122F39874983B52FF62F9C +:1024D000E72FF82E092F212F398549815B2F6F2F88 +:1024E0007E2F8F2D902F08E00E943F402983F32FED +:1024F0006A017B01E82F192FD10197968D919C914C +:102500009897092E000CAA0BBB0B4C015D01BB0C6C +:102510008808982C54019C01AD01682D782D882DD8 +:10252000982D04E00E942640A980BF2E0E2F0E9405 +:102530005D400E94D34120E030E040E85BE30E9430 +:10254000B54208C060E070E080EC9FE703C060E047 +:1025500070E0CB016D960FB6F894DEBF0FBECDBF15 +:10256000DF91CF911F910F91FF90EF90DF90CF906F +:10257000BF90AF909F908F907F906F905F904F90A3 +:102580003F902F9008954F925F926F927F928F921B +:102590009F92AF92BF92CF92DF92EF92FF92CF9332 +:1025A000DF9300D000D000D0CDB7DEB79E838D83FF +:1025B0000E942C0F6DEF8D819E810E947C0DA0E00A +:1025C000B0E0811520E89207A105B10509F4D3C058 +:1025D000ED81FE8185849684A784B088FCE29F1AF1 +:1025E000F1E0AF0AB1083EE0880F991FAA1FBB1F98 +:1025F0003A95D1F7ED81FE8141A952A9052E000C33 +:10260000660B770B24E1440F551F661F771F2A9531 +:10261000D1F76C017D01C41AD50AE60AF70AA3A90D +:10262000B4A9A50194010E94EE3EA7019601261BC4 +:10263000370B480B590BCA01B901705C8F4F9F4F84 +:10264000412C30E8532E612C712CA30192010E9481 +:10265000C43E29833A834B835C83ED81FE81A0A92C +:10266000B0E0A50194010E94E33E20E038E040E0A4 +:1026700050E00E94C43E69017A01F0E8DF0EE11CDF +:10268000F11CED81FE81A5A90A2E000CBB0BA50152 +:1026900094010E94EE3E812C44E0942EA12CB12C9A +:1026A000A50194010E94C43ECA01B901A701960187 +:1026B0000E94863EA50194010E94C43E405E5F4F89 +:1026C000ED81FE81A6A5B7A50E94EE3E705E8F4FFC +:1026D0009F4F20E030E440E050E00E94C43E69811A +:1026E0007A818B819C810E94863E6B017C01A301D3 +:1026F00092010E94C43ECA01B9010E94863E9B011C +:10270000AC0167E055954795379527956A95D1F7C0 +:10271000ED81FE81A5A5B0E00E94E33E20E130E01E +:1027200040E050E00E94C43ED701C601821B930BDB +:10273000A40BB50BB7FF03C080E090E0DC01813053 +:102740009105A105F9E1BF0724F080E090E0A0E049 +:10275000B9E1BC01CD012CE09595879577956795FA +:102760002A95D1F70E94D94120E030E040E85AE3B1 +:102770000E94B54204C060E070E080EC9FE72696BE +:102780000FB6F894DEBF0FBECDBFDF91CF91FF90A3 +:10279000EF90DF90CF90BF90AF909F908F907F9001 +:1027A0006F905F904F900895CF92DF92EF92FF92DB +:1027B0006A017B010E94DD0F20E030E048EC52E42A +:1027C0000E942B41A70196010E942B4121E03EED82 +:1027D00042E45EE30E9422439B01AC0160E070E0B2 +:1027E00080E89FE30E94BE4020E03AE24DE257E4D9 +:1027F0000E94B542FF90EF90DF90CF900895CF9266 +:10280000DF92EF92FF920F931F93CB01BA01680101 +:10281000790120E03AE24DE257E40E942B419B010E +:10282000AC0160E070E080E89FE30E94BE4026EFCC +:1028300038E248EA50E40E9422439B01AC01C70100 +:10284000B6010E942B411F910F91FF90EF90DF90F6 +:10285000CF900895FC0161857285838594850895E4 +:10286000FC012189328943895489A5E0B0E00E94A6 +:10287000E33E672F782F892F992787FD9A950E942D +:10288000DB4120E030E048EC52E40E942B41089507 +:10289000CF93DF93EC01CB01BA0120E030E048ECAC +:1028A00052E40E94B5420E949D41982F872F762FB7 +:1028B000662725E030E040E050E00E94C43E298BCE +:1028C0003A8B4B8B5C8BDF91CF9108950F931F93C5 +:1028D000CF93DF938C01EB0188E2FB0111928A9583 +:1028E000E9F74BE050E06BE771E0CE010E94CA458A +:1028F0001B8681E090E0A0E0B0E08C879D87AE87EA +:10290000BF87F80180819181092E000CAA0BBB0BB7 +:10291000888B998BAA8BBB8B8DE090E0A0E0B0E018 +:102920008C8B9D8BAE8BBF8B1CA21DA21EA21FA2E7 +:1029300080E090E0A0E2B2EC8C8F9D8FAE8FBF8FD5 +:1029400080E090E0AAEAB2E4888F998FAA8FBB8FCB +:102950008AE097EDA3E2BCE388A399A3AAA3BBA353 +:10296000DF91CF911F910F910895CF93DF93EB01EA +:1029700024E2FB0111922A95E9F744E250E060E07D +:1029800070E0488359836A837B83FC018081918155 +:10299000092E000CAA0BBB0B8C839D83AE83BF83D7 +:1029A0008DE090E0A0E0B0E088879987AA87BB8798 +:1029B0000E94CB0A288B398B4A8B5B8B8AE994E087 +:1029C0000E942C0F6C8B7D8B8E8B9F8B81E0DF9117 +:1029D000CF9108950F931F93CF93DF938C01EB0159 +:1029E00088E2FB0111928A95E9F74BE050E06BE732 +:1029F00071E0CE010E94CA451B8681E090E0A0E014 +:102A0000B0E08C879D87AE87BF87F8018081918178 +:102A1000092E000CAA0BBB0B888B998BAA8BBB8B46 +:102A200086E090E0A0E0B0E08C8B9D8BAE8BBF8BFE +:102A30001CA21DA21EA21FA280E090E0A6E9B3E4A2 +:102A40008C8F9D8FAE8FBF8F80E090E8A9E8B4E4B3 +:102A5000888F998FAA8FBB8F86EA9BE9A4E4BCE399 +:102A600088A399A3AAA3BBA3DF91CF911F910F9134 +:102A70000895CF93DF93EB0124E2FB0111922A9595 +:102A8000E9F744E250E060E070E0488359836A83EC +:102A90007B83FC0180819181092E000CAA0BBB0B6A +:102AA0008C839D83AE83BF8386E090E0A0E0B0E09E +:102AB00088879987AA87BB870E94CB0A288B398B86 +:102AC0004A8B5B8B8AE994E00E94DD0F20E030E0C6 +:102AD00048EC52E40E942B416C8B7D8B8E8B9F8B3C +:102AE00081E0DF91CF9108950F931F93CF93DF93F0 +:102AF0008C01EB0188E2FB0111928A95E9F74BE02A +:102B000050E06BE771E0CE010E94CA451B8681E070 +:102B100090E0A0E0B0E08C879D87AE87BF87F8018A +:102B200080819181092E000CAA0BBB0B888B998B9D +:102B3000AA8BBB8B8CE090E0A0E0B0E08C8B9D8BEF +:102B4000AE8BBF8B1CA21DA21EA21FA21C8E1D8EAF +:102B50001E8E1F8E80E090E0A8ECB2E4888F998FE3 +:102B6000AA8FBB8F80E090E0A0E4B0E488A399A393 +:102B7000AAA3BBA3DF91CF911F910F910895CF938B +:102B8000DF93EB0124E2FB0111922A95E9F744E27D +:102B900050E060E070E0488359836A837B83FC01E6 +:102BA00080819181092E000CAA0BBB0B8C839D8325 +:102BB000AE83BF838CE090E0A0E0B0E08887998787 +:102BC000AA87BB870E94CB0A288B398B4A8B5B8BE9 +:102BD0008AE994E00E94C3126C8B7D8B8E8B9F8B55 +:102BE00081E0DF91CF910895CF93DF93EC010E94B4 +:102BF000DB071A828CE78CAB8AE08DAB8EE78EAB5D +:102C00008FEA8FAB88AF82EA89AF1AAE80E88BAFCC +:102C1000DF91CF910895CF93DF9300D0CDB7DEB78A +:102C20004A83698342E0BE016F5F7F4F0E94BE0806 +:102C30000F900F90DF91CF9108950F93CF93DF9373 +:102C40001F92CDB7DEB79A01698301E041E0BE0172 +:102C50006F5F7F4F0E9406090F90DF91CF910F9118 +:102C600008950F93CF93DF931F92CDB7DEB79A01EC +:102C7000698302E041E0BE016F5F7F4F0E94060959 +:102C80000F90DF91CF910F9108950F931F93CF93E2 +:102C9000DF9300D0CDB7DEB78A01AE014F5F5F4F43 +:102CA0000E943116882341F029813A813227232757 +:102CB0003227F801318320830F900F90DF91CF915D +:102CC0001F910F9108950F93CF93DF931F92CDB76C +:102CD000DEB76983022F9E012F5F3F4F41E0B901AC +:102CE0000E9406090F90DF91CF910F91089540EF58 +:102CF00060E10E940B16EFE2F5E73197F1F700C0B3 +:102D0000000008950F931F93CF93DF9300D0CDB7AA +:102D1000DEB78C01AE014F5F5F4F60E00E9431165D +:102D20008FE295E70197F1F700C0000089819A8151 +:102D30008036E1E09E0721F4F801128281E008C0AC +:102D40008136914021F481E0F801828301C080E066 +:102D50000F900F90DF91CF911F910F9108950F93D6 +:102D60001F93CF93DF931F92CDB7DEB78C0140E066 +:102D700062E10E940B16811108C08FE295E701976E +:102D8000F1F700C0000080E016C04CEC62E1C80121 +:102D90000E940B16882389F38FE295E70197F1F7DC +:102DA00000C00000AE014F5F5F4F60E2C8010E94AB +:102DB0001D16882311F30F90DF91CF911F910F9172 +:102DC00008950F931F93CF93DF9300D01F92CDB739 +:102DD000DEB78C010E94AF16882359F18FE295E788 +:102DE0000197F1F700C000004EE062E1C8010E94C7 +:102DF0000B168823F1F023E0AE014F5F5F4F6CE4C8 +:102E0000C8010E9463168823A1F02981F801238359 +:102E10009A8194839B81958391E0273008F490E018 +:102E2000F8019283EFE2F5E73197F1F700C0000077 +:102E300007C08FE295E70197F1F700C0000080E03E +:102E40000F900F900F90DF91CF911F910F910895E8 +:102E5000633021F4FC012281222359F0462F60E1E6 +:102E60000E940B16EFE2F5E73197F1F700C0000082 +:102E7000089580E00895CF93DF93EC0163E50E940D +:102E8000E6078FE295E70197F1F700C00000CE0159 +:102E90000E9477168823A9F0CE010E94821688230B +:102EA00081F061E0CE010E942817882351F0CE0105 +:102EB0000E94AF16882329F0CE01DF91CF910C94A8 +:102EC000E11680E0DF91CF910895CF93DF936115F4 +:102ED0007105D9F0EC017F836E8361E00E942817B1 +:102EE000882399F0CE010E94AF16882371F042EC3E +:102EF00062E1CE010E940B16882339F0EFE2F5E77C +:102F00003197F1F700C0000007C08FE295E7019705 +:102F1000F1F700C0000080E0DF91CF9108956F923B +:102F20007F928F929F92AF92BF92CF92DF92EF9259 +:102F30000F931F93CF93DF931F92CDB7DEB73C0162 +:102F4000CB01342FE02FAFE2B5E71197F1F700C0C6 +:102F5000000068E170E00E94B03E8B014FEF460F29 +:102F60004295440F440F407C311140622111406171 +:102F7000E1114860E110446060E4C3010E940B1657 +:102F8000882309F471C0A80141505109569547950D +:102F90005695479561E4C3010E940B16882309F4F6 +:102FA00063C0A6015695479562E4C3010E940B16C3 +:102FB000882309F459C0A5015695479563E4C301D8 +:102FC0000E940B16882309F44FC0A401569547951B +:102FD00064E4C3010E940B16811146C049895A89D5 +:102FE0005695479565E4C3010E940B1681113CC0BC +:102FF000F3014681415066E4C3010E940B16811122 +:1030000033C0D30116968D919C911797019711F4B7 +:1030100040E801C040E067E4C3010E940B1688232A +:1030200019F1EFE2F5E73197F1F700C00000AE01CA +:103030004F5F5F4F6FE4C3010E941D168823A1F00C +:10304000AFE2B5E71197F1F700C00000F301268168 +:103050003781B901606C498150E06417750729F028 +:103060002F5F3F4F3783268380E00F90DF91CF9112 +:103070001F910F91EF90DF90CF90BF90AF909F90F6 +:103080008F907F906F900895AB0160E20C941D16B5 +:103090000F93CF93DF931F92CDB7DEB79B0141E231 +:1030A000498305E041E0BE016F5F7F4F0E94060942 +:1030B0000F90DF91CF910F9108950F931F93CF93AE +:1030C000DF93CDB7DEB729970FB6F894DEBF0FBEFA +:1030D000CDBF8C016623F1F0AE01475F5F4F60E228 +:1030E000C8010E941D168823E9F087EB9BE0019739 +:1030F000F1F700C00000898581FFEECF898581FF4F +:1031000011C027E0AE014F5F5F4F61E2C8010E942E +:10311000631608C0AE01475F5F4F60E20E941D1654 +:103120008111ECCF80E029960FB6F894DEBF0FBE78 +:10313000CDBFDF91CF911F910F9108950F931F93F2 +:10314000CF93DF93CDB7DEB729970FB6F894DEBFE4 +:103150000FBECDBF8C016623C1F087EB9BE00197CA +:10316000F1F700C00000AE01475F5F4F60E2C801A9 +:103170000E941D16811102C080E00AC1898580FF6E +:10318000ECCF898580FFF8CF09C0AE01475F5F4F64 +:1031900060E20E941D168111F4CFEECF28E0AE014F +:1031A0004F5F5F4F68E4C8010E946316882321F3D4 +:1031B00069817A8180E090E00E94D94120E030E08E +:1031C00040E05AE30E94B5429B01AC0160E070E030 +:1031D00080E090E40E9422430E94A441F8016787A6 +:1031E000708B818B928B6B817C8180E090E00E9460 +:1031F000D94120E030E040E05AE30E94B5429B0113 +:10320000AC0160E070E080E090E40E9422430E9404 +:10321000A441F801678B708F818F928F6D817E81C1 +:1032200080E090E00E94D94120E030E040E05AE3A5 +:103230000E94B5429B01AC0160E070E080E090E448 +:103240000E9422430E94A441F801678F70A381A3CA +:1032500092A36F81788580E090E00E94D94120E0C0 +:1032600030E040E05AE30E94B5429B01AC0160E0CF +:1032700070E080E090E40E9422430E94A441F801A3 +:1032800067A370A781A792A728E0AE014F5F5F4FA9 +:1032900068E2C8010E946316882309F46DCF698132 +:1032A0007A8180E090E00E94D94120E030E040E067 +:1032B0005AE30E94B5429B01AC0160E070E080E0FF +:1032C00090E40E9422430E94A441F801638B748B16 +:1032D000858B968B6B817C8180E090E00E94D94148 +:1032E00020E030E040E05AE30E94B5429B01AC018F +:1032F00060E070E080E090E40E9422430E94A441DC +:10330000F801638F748F858F968F6D817E8180E049 +:1033100090E00E94D94120E030E040E05AE30E9472 +:10332000B5429B01AC0160E070E080E090E40E9457 +:1033300022430E94A441F80163A374A385A396A32A +:103340006F81788580E090E00E94D94120E030E0F4 +:1033500040E05AE30E94B5429B01AC0160E070E09E +:1033600080E090E40E9422430E94A441F80163A7F8 +:1033700074A785A796A7AE014F5F5F4F68E3C801AA +:103380000E941D16882309F4F7CE9981F80193ABAA +:1033900029960FB6F894DEBF0FBECDBFDF91CF9157 +:1033A0001F910F910895CF93DF9300D000D0CDB738 +:1033B000DEB769837A834B835C8324E0AE014F5F81 +:1033C0005F4F63E10E9469080F900F900F900F907C +:1033D000DF91CF9108958F929F92AF92BF92CF923B +:1033E000DF92EF92FF920F931F93CF93DF93EC0145 +:1033F0004A015B01C901B80120E030E040E054E43B +:103400000E94B5420E94A4416B017C0123E333E991 +:1034100048E853E4C501B4010E94BF4020E030E019 +:1034200040E852E40E94B5420E94A441A601CE01A8 +:10343000DF91CF911F910F91FF90EF90DF90CF9090 +:10344000BF90AF909F908F900C94D319FC0187810F +:1034500090858330910540F4880F991FFC01EC5949 +:10346000FE4F8081918108958CE095E00895289821 +:1034700020981CBC2C9824B12F7024B921B12F7C2A +:1034800021B922B12F7C22B9FC011682089560E097 +:1034900070E0CB010E949F0A0196C9F30895611163 +:1034A00002C00E94471A8FEF08956EBD0DB407FE4B +:1034B000FDCF8EB50895289A08952898089595B15E +:1034C00081E0892785B9089514980895149A08957C +:1034D00083B186FB882780F9089580B185FB882712 +:1034E00080F908952AE0FC0121871E9B06C0FC019B +:1034F00021852111FACF80E0089581E0089524E626 +:10350000FC012187059906C0FC0121852111FACF14 +:1035100080E0089581E0089524E6FC012187059B61 +:1035200006C0FC0121852111FACF80E0089581E0D9 +:1035300008950F931F93CF93DF93EC01162F0E94F2 +:10354000641ACE010E94721A082F612F6F73CE0188 +:103550000E94551ACE010E94661A802FDF91CF91EA +:103560001F910F910895FF920F931F93CF93DF93B5 +:10357000EC01062FF42E0E94641ACE010E94721AEA +:10358000182F602FCE010E94551A6F2DCE010E9478 +:10359000551A8A838D8181608D83CE010E94661ABF +:1035A0008A8580FF2BC01F92FF921F920F9382E9A2 +:1035B00094E09F938F930E9467460F900F900F9017 +:1035C0000F900F900F90112389F08D8182FF0EC014 +:1035D0008A811F928F9383E894E09F938F930E9438 +:1035E00067460F900F900F900F9008C08EE794E001 +:1035F0009F938F930E9467460F900F90812FDF91CA +:10360000CF911F910F91FF900895AF92BF92CF92EB +:10361000DF92EF92FF920F931F93CF93DF93EC0112 +:10362000B42ED52E022F6F73162F10640E94641AC9 +:10363000CE010E94721AC82E612FCE010E94551A27 +:10364000EB2CFD2C5701D02EDD2041F0F5016191CE +:103650005F01CE010E94551ADA94F6CFCE010E9486 +:10366000661A8A8581FF46C01F731F921F9382E6E8 +:1036700094E09F938F930E9467460F900F900F9056 +:103680000F90102F8AE5A82E84E0B82E112379F030 +:10369000F70181917F011F928F93BF92AF920E9499 +:1036A000674611500F900F900F900F90EFCF85E568 +:1036B00094E09F938F930E9467460F900F90CC20C9 +:1036C00089F08D8182FF0EC08A811F928F9386E4DC +:1036D00094E09F938F930E9467460F900F900F90F6 +:1036E0000F9008C081E494E09F938F930E946746F7 +:1036F0000F900F908C2DDF91CF911F910F91FF9024 +:10370000EF90DF90CF90BF90AF900895EF92FF922F +:103710000F931F93CF93DF93EC017A016F73162FF2 +:1037200010680E94641ACE010E94721A082F612F3D +:10373000CE010E94551A8A838D8181608D8360E05D +:10374000CE010E94551AF7018083CE010E94661AAD +:103750008A8582FF2EC01F731F921F9385E294E01B +:103760009F938F930E9467460F900F900F900F903A +:103770000023B9F08D8180FF14C08A811F928F933E +:10378000F70180811F928F938FE094E09F938F9336 +:103790000E9467460F900F900F900F900F900F9020 +:1037A00008C08AE094E09F938F930E9467460F9031 +:1037B0000F90802FDF91CF911F910F91FF90EF908D +:1037C0000895AF92BF92CF92DF92EF92FF920F9344 +:1037D0001F93CF93DF938C01D62FB42ED52EC22FFB +:1037E0000E94641AC8010E94721AC82E6D2F606C64 +:1037F000C8010E94551AEB2CFD2C5701DC2EDD2050 +:1038000049F060E0C8010E94551AF50181935F01FB +:10381000DA94F5CFC8010E94661AF801828583FF09 +:1038200031C01F92CF93DF731F92DF9383EE93E03B +:103830009F938F930E9467460F900F900F900F9069 +:103840000F900F90CC20B1F00BED13E0CC2379F06A +:10385000F70181917F011F928F931F930F930E9415 +:103860006746C1500F900F900F900F90EFCF89EDEA +:1038700093E002C084ED93E09F938F930E9467468C +:103880000F900F908C2DDF91CF911F910F91FF9092 +:10389000EF90DF90CF90BF90AF90089521E00C940F +:1038A000E11B9B01205331092E30310510F40C949B +:1038B0004E1C80E00895AF92BF92CF92DF92EF92BC +:1038C000FF920F931F93CF93DF93CDB7DEB762972D +:1038D0000FB6F894DEBF0FBECDBF8C01242F8AE057 +:1038E000F8018187AB016FE3C8010E94051BF82E28 +:1038F000AE014E5E5F4F6AE370E0C8010E944E1C4D +:103900009A899431A1F58F2191F165E370E0C801A6 +:103910000E94991AF82E882351F1D12CE12CAE0186 +:103920004F5E5F4F65E370E0C8010E944E1CF822B5 +:103930008989D81659F0FFE0FE1540F0E1E0F0E08B +:10394000EC0FFD1FEE0DF11D8083E394FF2049F085 +:10395000833129F0F8019185D82E9111E0CFFF2411 +:10396000F39491E0833109F090E0F92241F56BE3A3 +:1039700070E0C8010E94991A80E009C080EC93E0D1 +:103980009F938F930E9467460F900F9081E0817004 +:1039900062960FB6F894DEBF0FBECDBFDF91CF9118 +:1039A0001F910F91FF90EF90DF90CF90BF90AF905D +:1039B0000895222389F0F8018185882369F0AE01FA +:1039C0004E5E5F4F6AE370E0C8010E944E1C2A8978 +:1039D0008111EFCF80E001C081E091E0211190E002 +:1039E000892329F2AE014F5E5F4F65E370E0C801A5 +:1039F0000E944E1C2989882339F0213039F0F801C2 +:103A000081858111EFCF02C080E001C081E091E0AB +:103A1000213009F090E0892309F4B9CF8BEC93E0D1 +:103A20009F938F930E946746CE0101965C010F9091 +:103A30000F908C0183ECC82E83E0D82E802F8A193A +:103A40008E1508F09BCFF80181918F011F928F9303 +:103A5000DF92CF920E9467460F900F900F900F90C9 +:103A6000EDCF3F924F925F926F927F928F929F9293 +:103A7000AF92BF92CF92DF92EF92FF920F931F937C +:103A8000CF93DF93CDB7DEB767970FB6F894DEBF5D +:103A90000FBECDBF6C01611105C046E062E00E941F +:103AA000B31A14C1623008F010C189E696E09F9302 +:103AB0008F930E9467460F900F9080E090E0EE2475 +:103AC000EA94FE2C512C34E1432E49EF642E45E05C +:103AD000742E53EF852E55E0952E2FEF30E03E8B60 +:103AE0002D8B5C013FEFA31AB30A9F938F9387E25C +:103AF00096E09F938F930E946746AE014B5E5F4FA7 +:103B000065E370E0C6010E944E1C0F900F900F906D +:103B10000F90811109C024E236E03F932F930E9459 +:103B200067460F900F900EC08E898F938D898F936B +:103B3000EDE1F6E0FF93EF930E9467460F900F9040 +:103B40000F900F908F8989831F925F921F924F92DF +:103B50008FEF95E09F938F930E9467468E010E5FD3 +:103B60001F4F0F900F900F900F900F900F90312CD0 +:103B7000832D8A0DF80181938F011F928F937F927D +:103B80006F920E94674633940F900F900F900F90A2 +:103B9000F3E13F12EDCF9F928F920E94674644E17E +:103BA000BE016F5F7F4FC6010E945B1C0F900F909C +:103BB000811171C084E690E00E940B0A80EF95E0CD +:103BC0009F938F930E9467460F900F902FEFE216FE +:103BD000F20609F45EC03BE2E316F10429F08DE23F +:103BE000E816F10431F00CC091E0951558F45A94A0 +:103BF0000EC0552051F0E9E0E51548F0539407C098 +:103C0000512C05C05524539402C08AE0582EF5E08B +:103C10005F9E800111240B52194F41E050E0B80122 +:103C2000CE0147960E94A84586E596E09F938F9324 +:103C30000E9467460F5F1F4F1F930F930E946746B6 +:103C400081E596E09F938F930E9467464F896EE3CC +:103C5000C6010E94B31A0F900F900F900F900F9013 +:103C60000F90882371F08F891F928F938EE396E0D7 +:103C70009F938F930E9467460F900F900F900F9025 +:103C800008C088E396E09F938F930E9467460F9049 +:103C90000F90C50122CF84E690E00E94200A60EDDB +:103CA00077E080E090E00E94BA0A7C012DEE35E0DA +:103CB0003F932F930E9467460F900F909BE1E91668 +:103CC000F10409F083CF80E001C08FEF67960FB653 +:103CD000F894DEBF0FBECDBFDF91CF911F910F9142 +:103CE000FF90EF90DF90CF90BF90AF909F908F901C +:103CF0007F906F905F904F903F9008958F929F922A +:103D0000AF92BF92CF92DF92EF92FF920F931F93E9 +:103D1000CF93DF9300D0CDB7DEB78C014B015A01B2 +:103D2000C22EFA01108282E3F801818785818B7FA0 +:103D30008583AE014E5F5F4F6BE370E0C8010E9468 +:103D40004E1CF82E882309F446C08A818F7709F427 +:103D500046C0D82E8FEA94E00197F1F700C000002A +:103D6000AE014E5F5F4F6BE370E0C8010E944E1CD6 +:103D7000882321F09A819F77E92E01C0ED2CF8016C +:103D8000218591E0211101C090E0F92EF82219F16E +:103D9000DE1411F0DE2CDECFF80185818270D82E82 +:103DA00090E044962E2D30E08217930769F19F939F +:103DB0008F93F50180811F928F9385E993E09F9304 +:103DC0008F930E946746F50110820F900F900F901D +:103DD0000F900F900F90F12C02C0E11056C06AE3D3 +:103DE00070E0C8010E94991A8F210F900F90DF9107 +:103DF000CF911F910F91FF90EF90DF90CF90BF90E8 +:103E0000AF909F908F9008959F938F938EE793E04C +:103E10009F938F930E9467460F900F900F900F9083 +:103E2000F4E1FC1518F42E2D2D1901C02C2DF501EF +:103E30002083A4016FEFC8010E94E11BF5018111ED +:103E400002C01082C8CF8081E81ADE1430F684E602 +:103E500093E09F938F930E9467460F900F90AE015F +:103E60004F5F5F4F6FEFC8010E94861BEA94DE141C +:103E7000B0F3DD2009F4AFCF22E0A8014D5F5F4F22 +:103E80006FEFC8010E94E11BA6CF22E0A8014D5FA1 +:103E90005F4F6FEFC8010E94E11BF82E882309F4E1 +:103EA0009ACFF80185818460858399CF3F924F92A4 +:103EB0005F926F927F928F929F92AF92BF92CF92BA +:103EC000DF92EF92FF920F931F93CF93DF93CDB7C3 +:103ED000DEB767970FB6F894DEBF0FBECDBF8C017B +:103EE000611105C046E062E00E94B31A61C1623010 +:103EF00008F05DC180E090E07C012FEFE21AF20A49 +:103F00009F938F9386ED95E09F938F930E946746D2 +:103F100064E370E0C8010E94991A0F900F900F900F +:103F20000F908FEF90E09E8B8D8BAE014B5E5F4FBD +:103F300065E370E0C8010E944E1C81110EC083ED44 +:103F400095E09F938F930E9467469FE7EFE4F2E12D +:103F50009150E040F040E1F714C060E070E0CB0128 +:103F60000E949F0A019691F089EC95E09F938F93B0 +:103F70000E9467468FE79FE4E2E181509040E04075 +:103F8000E1F700C000000F900F9019C08D899E8945 +:103F90008130910571F480EC95E09F938F930E949E +:103FA0006746FFE72FE482E1F15020408040E1F7CF +:103FB000E8CF8D30910519F0419709F0B2CF8D8986 +:103FC0009E898D30910521F0419711F0C70194CF62 +:103FD0008FEA95E09F938F930E9467460F900F9012 +:103FE00080E6482E85E0582E94E5692E95E0792EDE +:103FF00026E7C22E25E0D22E3EE7A32E35E0B32ED3 +:104000004FE9842E45E0942EAE014B5E5F4F65E391 +:1040100070E0C8010E944E1C81110EC0ECEAF5E070 +:10402000FF93EF930E9467468FE79FE4E2E18150A0 +:104030009040E040E1F72EC060E070E0CB010E94CC +:104040009F0A019691F082EA95E09F938F930E94D8 +:1040500067462FE78FE492E1215080409040E1F7DE +:1040600000C000000F900F90A2C0AE01495E5F4FEC +:104070006BE370E0C8010E944E1C81110EC09F923C +:104080008F920E946746EFE7FFE422E1E150F040A3 +:104090002040E1F700C0000071C08F89843108F42E +:1040A0006FC024E1AE01495E5F4FBE016F5F7F4F7D +:1040B000C8010E947E1E882309F462C08F8988236C +:1040C00009F45EC084E690E00E94200A8AE995E047 +:1040D0009F938F930E946746F80185810F900F9000 +:1040E00082FF24C084818F771F928F938381282F32 +:1040F000082E000C330B3F938F9383E895E09F933A +:104100008F930E946746F80184810F900F900F9063 +:104110000F900F900F9087FF03C0BF92AF9202C025 +:10412000DF92CF920E9467460F900F908F891F9267 +:104130008F935F924F920E9467460F900F900F905F +:104140000F90312C8F893816A8F4E1E0F0E0EC0FE5 +:10415000FD1FE30DF11D80811F928F938AE595E08D +:104160009F938F930E94674633940F900F900F9008 +:104170000F90E8CF7F926F920E9467460F900F904A +:104180008D899E898D30910509F43ECF813191054D +:1041900009F43ACF9F938F9380E495E09F938F9398 +:1041A0000E9467460F900F900F900F900FCF8FEFE8 +:1041B00067960FB6F894DEBF0FBECDBFDF91CF91EB +:1041C0001F910F91FF90EF90DF90CF90BF90AF9035 +:1041D0009F908F907F906F905F904F903F90089549 +:1041E000FC012681222389F0278130852130310589 +:1041F00031F038F02230310541F40C94561F0C9404 +:10420000311D611102C00E94471A8FEF0895AF92CD +:10421000BF92CF92DF92EF92FF920F931F93CF93B3 +:10422000DF93CDB7DEB729970FB6F894DEBF0FBE88 +:10423000CDBF7C0182E693E09F938F930E946746F7 +:1042400086EAC82E86E0D82E0F900F9010E097E5F2 +:10425000A92E93E0B92E41E050E0B601CE010996B7 +:104260000E94A845AE014F5F5F4F612FC7010E94BA +:10427000861B89859981981751F01F928F93BF9261 +:10428000AF920E9467460F900F900F900F901F5FA4 +:104290008FEFC81AD80A1F32F1F600E310E0AE0122 +:1042A0004F5F5F4FB801C7010E94511C0F5F1F4F46 +:1042B0000E331105A1F728E0AE014F5F5F4F6EE3AB +:1042C000C7010E94E11B29960FB6F894DEBF0FBE0E +:1042D000CDBFDF91CF911F910F91FF90EF90DF90B5 +:1042E000CF90BF90AF9008951F93CF93DF93EC01D1 +:1042F0000E94641A88E28A95F1F7CE010E94661A3C +:1043000088EC8A95F1F7CE010E94641ACE010E94D2 +:10431000721A182F60E370E0CE010E94991A81236F +:10432000DF91CF911F910895CF92DF92EF92FF928C +:104330001F93CF93DF931F92CDB7DEB76C0186EA50 +:10434000E82E86E0F82E10E041E050E0B701CE0103 +:1043500001960E94A8454981612FC6010E94B31AA7 +:10436000173039F4998192FF04C0F601958192606B +:1043700095831F5F882331F08FEFE81AF80A1F3208 +:1043800019F781E00F90DF91CF911F91FF90EF908F +:10439000DF90CF9008955F926F927F928F929F925D +:1043A000AF92BF92CF92DF92EF92FF920F931F9343 +:1043B000CF93DF93CDB7DEB728970FB6F894DEBF63 +:1043C0000FBECDBF4C018E010F5F1F4FE12CF12CB2 +:1043D00060E0702EC12E84E4A82E83E0B82EDD2488 +:1043E000D394D60E6E2C5F2C85E0689FB00111240B +:1043F0006B52794F41E050E0C8010E94A845EAE0C5 +:10440000ED1508F4D12CF401828584FF10C0F80169 +:1044100080811F928F935F926F92BF92AF920E94A2 +:1044200067460F900F900F900F900F900F90FFEF37 +:10443000EF1AFF0A0F5F1F4F88E0E816F10411F032 +:104440006D2DCDCF28E0472D5C2D6EE3C4010E9479 +:10445000051B28960FB6F894DEBF0FBECDBFDF91C7 +:10446000CF911F910F91FF90EF90DF90CF90BF9071 +:10447000AF909F908F907F906F905F900895CF93B3 +:10448000DF93EC012898209A8091640084708093D7 +:104490006400149A0C9A2C9A269884B1806B84B983 +:1044A00080E58CBD159A0D9889EF94E09F938F93CA +:1044B0000E946746CE010E9474210F900F908111D7 +:1044C00003C083EF94E01FC08CED94E09F938F9323 +:1044D0000E946746CE010E9494210F900F90811197 +:1044E00003C086ED94E00FC080EC94E09F938F931F +:1044F0000E946746CE010E94CB210F900F90811140 +:1045000007C08AEB94E09F938F930E9467460EC08A +:1045100087EB94E09F938F930E94674681E08E83A0 +:104520008FEF8A87CE010E9407211A860F900F9085 +:10453000DF91CF91089524B1287F24B925B1276058 +:1045400025B921E0FC0122830895FC01128284B187 +:10455000887F84B985B1887F85B90895EF92FF92ED +:104560000F931F93CF93DF9361112CC0EC010AE1ED +:1045700017E08CE0E82E87E0F82E6AE070E080E03B +:1045800090E00E949F0A0196F9F41F930F930E94F6 +:1045900067468B811F928F93282F082E000C330BB8 +:1045A0003F938F93FF92EF920E9467468DB79EB71D +:1045B00008960FB6F8949EBF0FBE8DBF1A99DDCF37 +:1045C0001B82DBCF8FEF01C080E0DF91CF911F9185 +:1045D0000F91FF90EF90089583B182FB882780F9B7 +:1045E00091E08927089583B180958170089583B102 +:1045F0008695817091E089270895FC01828181115F +:1046000002C01382089533B191E0392737953327DB +:104610003795330F330B23B1269521702927279522 +:1046200022272795220F220B80E030FB80F920FB08 +:1046300081F990911F0198279370D1F490911E01F8 +:1046400090FD0EC0982791FF0BC0938181FD04C09F +:104650009F3729F09F5F03C0903809F091509383F2 +:1046600080911E0130FB80F920FB81F980931E01AF +:1046700080911F0130FB80F920FB81F980931F019D +:104680000895FC01828193818330910540F4880F65 +:10469000991FFC01E659FE4F8081918108958AE9B6 +:1046A00091E00895E8EBF0E02DE020832CE12083F9 +:1046B00024E6208324E02093BC0020E620937C00A5 +:1046C00027E820937A0080579F4F21E0FC01208348 +:1046D000089580579F4FFC01108284E08093BC00B6 +:1046E0001092B80010927C0010927A0008952F92D8 +:1046F0003F924F925F926F927F928F929F92AF9272 +:10470000BF92CF92DF92EF92FF920F931F93CF93BE +:10471000DF93CDB7DEB761970FB6F894DEBF0FBE5B +:10472000CDBF8C016111A5C2FC01828193810097EC +:1047300009F0E8C180EB98E09F938F930E94674651 +:1047400098012C5F3F4F3D872C8767E7C9010E9486 +:104750001A0F0F900F90811103C08DEA98E08BC261 +:104760008DE998E09F938F930E94674658013DE33F +:10477000A30EB11CC5010E943B170F900F90811131 +:1047800003C08AE998E077C262E0C5010E94281759 +:10479000811103C087E998E06EC200E010E022E8D2 +:1047A00032E440E050E068EC71E4C5010E94EB198E +:1047B000811103C084E998E05EC281E998E09F938B +:1047C0008F930E9467460F900F908FEF8F83412C3D +:1047D000512C32014E865F86688A798A8C859D8548 +:1047E0000E94DD0F20E030E543EC57E40E942B41AE +:1047F0009F938F937F936F93EAE7F8E0FF93EF9394 +:104800000E9467468C859D850E942C0F688779875A +:10481000382E292E9F938F9339853F9388858F93C8 +:10482000EDE6F8E0FF93EF930E9467468C859D8547 +:104830000E94C3124B018A879B879F938F939F92FD +:104840006F9381E698E09F938F930E9467460FB61F +:10485000F894DEBF0FBECDBF3F813F3F09F19A857F +:104860008B85282D392D492F582FFC01682D792D46 +:104870008F2F9E2F0E947243811112C07885698507 +:10488000C101272F362F492F582FDB01F1016B2F44 +:104890007A2F8F2F9E2F0E947243882309F4FBC129 +:1048A00065E0C62ED12CE12CF12C05E010E025E0CE +:1048B00030E045E050E063E070E08C859D850E942B +:1048C000F00C8F81803208F413C1E4E5F8E0FF9327 +:1048D000EF930E9467460F900F903F8113162CF5BF +:1048E000632F330F770B880B990B0E94DB416B0111 +:1048F0007C019B01AC01C301B2010E942B414B0121 +:104900008A879B87A70196016E857F85888999890B +:104910000E942B4168877987382E292E412C512CF3 +:1049200032014E865F86688A798A9A858B85082D42 +:10493000192D292F382FF885E985C1014F2F5E2FBA +:10494000692F722DC5010E94EB19811105C080E508 +:1049500098E09F938F93C7C08B858F939A859F9381 +:104960009F928F922F923F92E985EF93F885FF9364 +:104970002EE338E03F932F930E94674662E0C50123 +:104980000E9428170FB6F894DEBF0FBECDBF81116D +:1049900003C08BE398E093C014E687EC9FEA01978D +:1049A000F1F700C00000BE016A5F7F4FC5010E94A1 +:1049B0004418882309F48AC08E8181FF87C0BE0114 +:1049C0006F5F7F4FC5010E944818882309F481C09A +:1049D00089811F928F93E2E3F8E0FF93EF930E94A7 +:1049E00067460F900F900F900F908981833091F060 +:1049F00038F4813061F0823099F483E298E012C09B +:104A0000843059F0853061F48BE098E00BC088E287 +:104A100098E008C08AE198E005C085E198E002C00E +:104A200089E098E09F938F930E9467460F900F90C4 +:104A30008B818F938A818F938BEF97E09F938F93D6 +:104A40000E9467468D818F938C818F9320EF37E092 +:104A50003F932F930E9467468C819D810FB6F894F7 +:104A6000DEBF0FBECDBF8039F1E09F0718F48EEE98 +:104A700097E01DC0883522E0920718F484EE97E095 +:104A800016C0803233E0930718F48FED97E00FC023 +:104A9000883EE3E09E0718F48AED97E008C08C3D5D +:104AA000954018F485ED97E002C081ED97E09F9363 +:104AB0008F930E9467460F900F908FEC97E09F9323 +:104AC0008F930E9467460F900F9003C0115009F01A +:104AD00064CF61E0C5010E942817811108C02CEC49 +:104AE00037E03F932F930E9467460F900F901F82ED +:104AF00068EE73E080E090E00E949F0A019609F45E +:104B00006DCEC1C08130910509F071C007581F4FAB +:104B100061E0C8010E94E607811103C089EC97E0BB +:104B2000AAC09AEA692E97E0792E20EAE22E27E0C1 +:104B3000F22E30E9C32E37E0D32E4FE7A42E47E004 +:104B4000B42E59E8852E57E0952E80917A00806426 +:104B500080937A0080917A0086FDFCCF8091790065 +:104B600089831F928F937F926F920E94674641E0E4 +:104B7000BE016F5F7F4FC8010E94BE080F900F906B +:104B80000F900F90811106C0FF92EF920E9467462E +:104B90000F900F90DF92CF920E94674641E0BE01D6 +:104BA0006F5F7F4FC8010E949B080F900F90882372 +:104BB00061F089811F928F939F928F920E94674626 +:104BC0000F900F900F900F9006C0BF92AF920E946F +:104BD00067460F900F9068EE73E080E090E00E94CF +:104BE0009F0A019609F4B1CF0E94F0074CC00297CA +:104BF00009F03FC005581F4F40E061E0C8010E9426 +:104C00004F09811103C08CE797E035C086E4C82EB8 +:104C100087E0D82EC8010E949F097C010196F1F01F +:104C200080917A00806480937A0080917A0086FD7A +:104C3000FCCF60917900C8010E947F0980917900C2 +:104C40001F928F93FF92EF92DF92CF920E9467465E +:104C50000F900F900F900F900F900F9060E070E00A +:104C6000CB010E949F0A0196A9F2C8010E945F0928 +:104C70000AC082E497E09F938F930E9467460F904B +:104C80000F908FEF29C064EF71E080E090E00E9408 +:104C90009F0A80E021C078856985C101272F362FC2 +:104CA000492F582F6E857F85888999890E94BF403A +:104CB0006E877F87888B998B9A858B85282D392DD8 +:104CC000492F582FC301B2010E94BF402B013C0164 +:104CD0008F818F5F8F83E4CD61960FB6F894DEBF2E +:104CE0000FBECDBFDF91CF911F910F91FF90EF903D +:104CF000DF90CF90BF90AF909F908F907F906F90FC +:104D00005F904F903F902F900895FC012281338156 +:104D10002230310541F4579A5F9A85589F4F0E947F +:104D2000A4095F9808958091BC0080688093BC00BE +:104D3000089520E620937C0027E820937A00289AA3 +:104D4000299824B1236024B95A9A22E02093C800FC +:104D500028E92093C90026E02093CA001092CD00D4 +:104D60002BE92093CC0021E0FC0122830895FC0173 +:104D7000128210927C0010927A001092C800109259 +:104D8000C9001092CA001092CD001092CC005A981F +:104D900084B18C7F84B985B18C7F85B90895CF9219 +:104DA000DF92EF92FF920F931F93CF93DF938C01CB +:104DB00061114EC02898299887E0E82E89E0F82EE6 +:104DC000CC24CA94DC2CCCEED8E064EF71E080E017 +:104DD00090E00E949F0A019609F044C080917A00F9 +:104DE000806480937A0080917A0086FDFCCF809168 +:104DF000C80080648093C800289A299A809179001D +:104E00008093CE008091C80086FFFCCF2998289817 +:104E1000809179001F928F93FF92EF920E946746D4 +:104E2000F894F80183819481D482C38278940F909E +:104E30000F900F900F9097FDC8CF9F938F93DF93A4 +:104E4000CF930E9467460F900F900F900F90BDCFA9 +:104E500088EE98E09F938F930E9467460F900F9083 +:104E60008FEF01C080E0DF91CF911F910F91FF90F4 +:104E7000EF90DF90CF90089570E0FC01748363831E +:104E800008955F9A08955F9808955E9A08955E98D0 +:104E90000895139A0895139808958FEF84B908958B +:104EA00080918804813019F48FEF84B9089514B883 +:104EB000089580918804813041F487EB9BE001974D +:104EC000F1F700C0000080E0089583B1089565B94E +:104ED0000895109A0895109808951F93CF93DF9323 +:104EE000EC01162F0E944B278FEF84B98A8188230B +:104EF00089F0812F807F85B9CE010E9449278CE3FC +:104F00008A95F1F7CE010E944B2784E08A95F1F74C +:104F10001295107F15B9CE010E9449278CE38A951E +:104F2000F1F7CE010E944B2784E08A95F1F7DF91DB +:104F3000CF911F9108951F93CF93DF93EC01162F0C +:104F40000E944727CE010E944327612FCE01DF91A7 +:104F5000CF911F910C946D27EF92FF920F931F93A7 +:104F6000CF93DF93EC017B018B8187FD59C080914A +:104F70008804813041F487EB9BE00197F1F700C092 +:104F8000000081E04EC0CE010E9450278FEF85B90E +:104F9000CE010E944527CE010E94432794E09A95B6 +:104FA000F1F7CE010E9449278CE38A95F1F7CE01F3 +:104FB0000E945927082FCE010E944B2794E09A9512 +:104FC000F1F7CE010E9449278CE38A95F1F7CE01D3 +:104FD0000E94592782958F70007F182F102BCE01C9 +:104FE0000E944B2794E09A95F1F7CE010E94472743 +:104FF000912F90788BE0E816F10430F08BE0E81AFE +:10500000F10811F09111C4CF8B81813021F49923E3 +:1050100011F08BEF8B8315B88FEF84B981E091117C +:1050200080E0DF91CF911F910F91FF90EF90089555 +:10503000CF93DF93EC0162E370E00E94AC27811113 +:1050400002C08AEF8B83DF91CF9108951F93CF9396 +:10505000DF93EC01162F0E941828612F6068CE01A3 +:105060000E949B27CE01DF91CF911F910C941828AD +:105070001F93CF93DF93EC0115B88FEF84B98FE7BF +:105080009BEB0197F1F700C000001B8210E08A81C2 +:10509000882311F068E201C068E0CE010E949B27DE +:1050A000111105C087E99AE30197F1F704C08BE27B +:1050B00091E00197F1F700C000001F5F143039F74D +:1050C0006CE0CE010E949B2762E370E0CE010E945B +:1050D000AC27811102C08FEF1BC06CE0CE010E9493 +:1050E0009B2762E370E0CE010E94AC27811102C0D1 +:1050F0008DEF0EC061E0CE010E949B2760EB74E053 +:10510000CE010E94AC27811102C08CEF01C081E06A +:105110008B83DF91CF911F910895CF93DF93EC01A3 +:1051200081B1896081B98FEF84B98AB1806C8AB905 +:10513000CE010E9438288AE499E09F938F930E94C1 +:1051400067468B810F900F90813049F486E499E097 +:105150009F938F930E9467460F900F9010C0282F47 +:10516000082E000C330B3F938F938CE399E09F93B1 +:105170008F930E9467460F900F900F900F90CE0173 +:10518000DF91CF910C946927CF93DF93FC012381AA +:10519000213061F4EC010E9418286CE0CE010E94DD +:1051A0009B27CE01DF91CF910C941828DF91CF91EE +:1051B0000895CF93DF93FC012381213061F4EC014A +:1051C0000E94182868E0CE010E949B27CE01DF9143 +:1051D000CF910C941828DF91CF910895CF93DF934E +:1051E000FC012381213061F4EC010E94182861E068 +:1051F000CE010E949B27CE01DF91CF910C941828FD +:10520000DF91CF9108950E94EE2882B1867F82B906 +:1052100081B1867F81B915B814B88BB18F738BB902 +:105220008AB18F738AB90895CF93DF93EC018B8194 +:105230008130B9F44431A8F4613039F048F062307B +:1052400031F0633071F44C5A03C0405C01C04C5ED5 +:10525000642FCE010E942628CE01DF91CF910C94BD +:105260001828DF91CF9108951F93CF93DF93FC010E +:1052700023812130A1F4162FEC010E944727CE0193 +:105280000E944127612FCE010E946D27CE010E940E +:105290004327CE01DF91CF911F910C941828DF9105 +:1052A000CF911F910895FF920F931F93CF93DF9398 +:1052B0008C0161114FC0C0E084E1F82ED0E2DC0F18 +:1052C0008C2F6F2D0E94963E911105C040E0682FF3 +:1052D000C8010E9414296D2FC8010E943429C801F9 +:1052E0000E941828CF5FC03549F787E399E09F9364 +:1052F0008F930E946746F80183810F900F90813051 +:1053000049F484E399E09F938F930E9467460F903E +:105310000F9010C0282F082E000C330B3F938F9353 +:105320008AE299E09F938F930E9467460F900F90B7 +:105330000F900F9061E070E080E090E00E949F0A83 +:105340000196C1F364EF71E080E090E00E949F0A53 +:1053500080E009C086E299E09F938F930E946746A0 +:105360000F900F908FEFDF91CF911F910F91FF90D2 +:1053700008950F931F93CF93DF93EC018B01F801F6 +:1053800061918F01662339F08B81813021F4CE0148 +:105390000E943429F4CFDF91CF911F910F9108958E +:1053A0008BB18F708BB98AB1806F8AB908958AB139 +:1053B0008F708AB98BB18F708BB908958130910548 +:1053C00049F030F08230910539F0039739F00895B3 +:1053D0005C9A08955D9A08955E9A08955F9A08957B +:1053E000CB0141110C94DE296130710551F038F088 +:1053F0006230710541F06330710539F008955C98B1 +:1054000008955D9808955E9808955F9808956130B5 +:10541000710559F038F06230710551F06330710553 +:1054200059F008959BB180E105C09BB180E202C0B4 +:105430009BB180E4892702C08BB180588BB9089555 +:10544000CB010C94DE2940E00C94F029CF9360311D +:10545000E8F5C62FC370C23091F0C330B9F0C13047 +:1054600039F063E070E00E94232A80E090E014C0ED +:1054700060E070E00E94232A81E090E00DC061E0CE +:1054800070E00E94232A82E090E006C062E070E0B3 +:105490000E94232A83E090E00E94DE296C2F70E0B6 +:1054A0006F5F7F4F7F936F9389E599E09F938F9311 +:1054B0000E94674664EF71E080E090E00E949F0ADE +:1054C0000F900F900F900F9080E001C08FEFCF9161 +:1054D00008950895FC0112821092C8001092C9002C +:1054E0001092CA001092CD001092CC005A9884B14C +:1054F0008C7F84B985B18C7F85B908952F923F92B6 +:105500004F925F926F927F928F929F92AF92BF92D3 +:10551000CF92DF92EF92FF920F931F93CF93DF937F +:1055200000D000D0CDB7DEB79C838B83611121C042 +:10553000289A299884B1836084B95A9A82E080932A +:10554000C80088E98093C90086E08093CA00109261 +:10555000CD008BE98093CC0081E0EB81FC818283DC +:1055600082E49AE09F938F930E9467460F900F907A +:105570003DC18FEF860F843008F06EC0633039F084 +:10558000643041F0623051F41EE801E009C015E5D5 +:1055900000E002C01AEA01E0F12C04C011E000E0D2 +:1055A000FF24F3941F921F931F92FF921F920F9359 +:1055B00083E29AE09F938F930E9467460FB6F89418 +:1055C000DEBF0FBECDBFFF2011F0289A01C0289882 +:1055D000002311F0299A01C02998EBE2F1E03197FC +:1055E000F1F700C00000EB81FC8113821093CE0024 +:1055F00087EB9BE00197F1F700C0000083818823CF +:1056000089F084811F928F9383E19AE09F938F9317 +:105610000E946746EB81FC8113820F900F900F90E0 +:105620000F9008C082E09AE09F938F930E94674694 +:105630000F900F9088EE99E09F938F930E9467469A +:105640000F900F906FEF7FEFCB010E949F0A0196A2 +:10565000C9F32998289ACAC0653009F0BDC08BEA01 +:1056600099E09F938F930E946746299A28988BE22E +:1056700091E00197F1F700C000000F900F908DE9C5 +:10568000282E89E0382E95E9492E99E0592E27E8F1 +:10569000622E29E0722E35E8832E39E0932E42E8FF +:1056A000E42E49E0F42E5AE7C52E59E0D52E6EE6D9 +:1056B000A62E69E0B62E299AEB81FC81138200E7C1 +:1056C00011E0F8018091C80080648093C800819146 +:1056D0008093CE008091C80085FFFCCF21E0E837A1 +:1056E000F20781F78091C80086FFFCCF29983F928E +:1056F0002F920E9467460F900F90F80181918F01C1 +:105700001F928F935F924F920E9467460F900F9067 +:105710000F900F90F1E008371F0779F764E670E00B +:1057200080E090E00E949F0A9A8389837F926F9223 +:105730000E9467460F900F90EB81FC818381882344 +:1057400011F010E01FC09F928F920E9467460F9049 +:105750000F901DC0183031F4FF92EF920E946746FF +:105760000F900F90EB81FC81E10FF11D84811F925E +:105770008F93DF92CF920E9467461F5F0F900F902A +:105780000F900F90EB81FC818381181718F3EB8148 +:10579000FC818381803190F08289918920893785CD +:1057A0003F932F939F938F93BF92AF920E94674630 +:1057B0000F900F900F900F900F900F9089819A810A +:1057C0000196A1F468EE73E080E090E00E949F0AE9 +:1057D000019609F470CF0AC08AE699E09F938F93EF +:1057E0000E9467460F900F908FEF07C064EF71E043 +:1057F00080E090E00E949F0A80E00F900F900F9051 +:105800000F90DF91CF911F910F91FF90EF90DF905C +:10581000CF90BF90AF909F908F907F906F905F9050 +:105820004F903F902F900895FC012381203130F458 +:1058300031E0320F3383E20FF11D648308955798EE +:1058400010927C0010927A0014BC15BC24B1277E03 +:1058500024B91398FC011282089580B183FB882734 +:1058600080F991E0892708952C9808952C9A08953D +:10587000CF93DF93EC01579A80E680937C0087E812 +:1058800080937A0083E884BD84E085BD84B188611B +:1058900084B9139ACE010E94362C81E08A83DF916D +:1058A000CF9108952F923F924F925F926F927F9285 +:1058B0008F929F92AF92BF92CF92DF92EF92FF9220 +:1058C0000F931F93CF93DF936111E1C0EC0183ED40 +:1058D0009AE09F938F930E9467460F900F9083ECFE +:1058E000482E8AE0582E9FEA692E9AE0792E00EA27 +:1058F0001AE026E9E22E2AE0F22E3BE8C32E3AE037 +:10590000D32E4FE7A42E4AE0B42E5EE4252E5AE0B3 +:10591000352E67E6862E6AE0962E6AE070E080E01B +:1059200090E00E949F0A019609F0B3C080B183FB0A +:10593000882780F91F928F9388EC9AE09F938F932A +:105940000E9467460F900F900F900F90CE01039921 +:1059500005C00E94342C5F924F9206C00E94362CE4 +:105960008EEB9AE09F938F930E9467460F900F9063 +:1059700080917A00806480937A0080917A0086FD1D +:10598000FCCF809179001F928F937F926F920E943B +:10599000674682EE80937C008091790090E00597C5 +:1059A0000F900F900F900F9097FF02C080E090E053 +:1059B000809587BD1F928F931F930F930E94674618 +:1059C00080917A00806480937A000F900F900F90FE +:1059D0000F9080917A0086FDFCCF809179001F9214 +:1059E0008F93FF92EF920E94674690E690937C001F +:1059F00083B182FB882780F91F928F93DF92CF9229 +:105A00000E94674683B181701F928F93BF92AF92BD +:105A10000E946746F8946B817C8178948DB79EB71D +:105A20000C960FB6F8949EBF0FBE8DBF6115710521 +:105A300019F17F936F9380E090E00E94D9419B0120 +:105A4000AC0160E070E080E792E40E942B4127E126 +:105A500037EB41ED58E30E942B410E949D417F931B +:105A60006F939F928F920E9467460F900F900F90B6 +:105A70000F900F900F9051CF1F921F923F922F9235 +:105A80000E9467460F900F900F900F9046CF8FEFB8 +:105A900001C080E0DF91CF911F910F91FF90EF90B7 +:105AA000DF90CF90BF90AF909F908F907F906F903E +:105AB0005F904F903F902F90089583B182FB88278D +:105AC00080F991E08927089583B1817008958BB1A1 +:105AD00080588BB908951F93CF93DF93EC0123B1C6 +:105AE0008091DD049091DE04122F117020FD1BC007 +:105AF0002091E104222309F12091DF043091E00498 +:105B000023303105D0F0820F931F9C838B8310923A +:105B1000E0041092DF041092DE041092DD04CE0146 +:105B20000E94672D0AC0811520E4920748F501966E +:105B30009093DE048093DD040DC08091DF0490918A +:105B4000E004811520E49207F0F401969093E004BC +:105B50008093DF048091DD049091DE0481159044F0 +:105B600038F48091DF049091E0048115904410F0A6 +:105B70001C821B821093E104DF91CF911F91089545 +:105B800080E090E4D5CF80E090E4E0CFE4E6F0E080 +:105B9000808184708083179A0F9A2C9A84B1806BCD +:105BA00084B926988CB583658CBD0895269884B1F8 +:105BB0008F7584B90F9817981CBC0895179808958D +:105BC000179A08950F931F93CF93DF93EC01062F3D +:105BD000142F0E94DE2D80E48EBD0DB407FEFDCF94 +:105BE0000EBD0DB407FEFDCF1EBD0DB407FEFDCFEB +:105BF000CE010E94E02D84E18A95F1F780E0DF91EB +:105C0000CF911F910F9108951F93CF93DF93EC01D4 +:105C1000162F0E94DE2D81E48EBD0DB407FEFDCF50 +:105C20001EBD0DB407FEFDCF1EBC0DB407FEFDCF9B +:105C3000CE010E94E02D8EB5DF91CF911F91089586 +:105C4000CF93DF93C42FD22F1F924F931F926F9346 +:105C500086E69BE09F938F930E9467460F900F907C +:105C60000F900F900F900F90CD1769F08FE59BE08C +:105C70009F938F930E9467460F900F90CF3F59F4E8 +:105C800084E49BE002C080E49BE09F938F930E949A +:105C900067460F900F908EE39BE09F938F930E9437 +:105CA00067460F900F90DF91CF9108952F923F920A +:105CB0004F925F926F927F928F929F92AF92BF921C +:105CC000CF92DF92EF92FF920F931F93CF93DF93C8 +:105CD0006111D8C0EC018FE2282E8BE0382ECC2445 +:105CE000C394D12C2AE1422E2BE0522E35E0632EB4 +:105CF0003BE0732E40EFA42E4AE0B42E5BED852EE0 +:105D00005AE0952E64EF71E080E090E00E949F0AD7 +:105D1000019609F0B9C03F922F920E9467460F90FA +:105D20000F90E12CF12C86010E2C01C0000F0A947B +:105D3000EAF7402F409560E0CE010E94E22D402F0F +:105D400062E1CE010E94E22DFF92EF925F924F92AC +:105D50000E94674662E1CE010E94042E202F482F48 +:105D600062E1CE010E94202E68EC70E080E090E0BD +:105D70000E949F0A40E062E1CE010E94E22DFF9264 +:105D8000EF927F926F920E94674662E1CE010E947D +:105D9000042E20E0482F62E1CE010E94202E4FEF1A +:105DA00060E0CE010E94E22D68EC70E080E090E0BF +:105DB0000E949F0A8FEFE81AF80A8DB79EB70896DF +:105DC0000FB6F8949EBF0FBE8DBF98E0E916F104A0 +:105DD00009F0A9CF00E010E07601002E01C0EE0C22 +:105DE0000A94EAF74E2D409561E0CE010E94E22D23 +:105DF0004E2D63E1CE010E94E22D1F930F93BF92BF +:105E0000AF920E94674663E1CE010E94042E2E2DC0 +:105E1000482F63E1CE010E94202E68EC70E080E004 +:105E200090E00E949F0A40E063E1CE010E94E22DD3 +:105E30001F930F939F928F920E94674663E1CE015A +:105E40000E94042E20E0482F63E1CE010E94202E04 +:105E50004FEF61E0CE010E94E22D68EC70E080E03F +:105E600090E00E949F0A0F5F1F4F8DB79EB7089664 +:105E70000FB6F8949EBF0FBE8DBF0830110509F014 +:105E8000ABCF40CF8FEF01C080E0DF91CF911F916A +:105E90000F91FF90EF90DF90CF90BF90AF909F90C9 +:105EA0008F907F906F905F904F903F902F900895CC +:105EB00080E480937C0087E880937A0008951092B4 +:105EC0007C0010927A0008950F931F93CF93DF9375 +:105ED00061113AC088EA9BE09F938F930E946746C6 +:105EE0000F900F9003E91BE0C6E8DBE06AE070E08A +:105EF00080E090E00E949F0A019641F51F930F9366 +:105F00000E94674680917A00806480937A000F90A7 +:105F10000F9080917A0086FDFCCF2091780030911F +:105F2000790080917800909179003F932F939F930F +:105F30008F93DF93CF930E9467460F900F900F903F +:105F40000F900F900F90D2CF8FEF01C080E0DF91C4 +:105F5000CF911F910F91089582E480937C0087E890 +:105F600080937A00089510927C0010927A00089530 +:105F7000AF92BF92CF92DF92EF92FF920F931F9357 +:105F8000CF93DF9361117FC081EF9BE09F938F934D +:105F90000E9467460F900F903CEDE32E3BE0F32EFE +:105FA0000FEC1BE0CFEADBE06AE070E080E090E01D +:105FB0000E949F0A019609F068C0FF92EF920E942A +:105FC000674680917A00806480937A000F900F90EA +:105FD00080917A0086FDFCCF209178003091790085 +:105FE00080917800909179003F932F939F938F93A6 +:105FF0001F930F930E94674660917800709179001B +:1060000080E090E00E94D9412AE939E941E852E470 +:106010000E942B412DEC3CEC4CE05FE30E94BF4022 +:10602000D62EC72EB82EA92EA6019501652F742F46 +:10603000832F922F0E94A441862F90E061701F92BF +:106040006F9381FB222720F91F922F9382FB222737 +:1060500020F91F922F9323E0959587952A95E1F7D4 +:106060009F938F93AF92BF92CF92DF92DF93CF93A4 +:106070000E9467468DB79EB744960FB6F8949EBFB0 +:106080000FBE8DBF91CF8FEF01C080E0DF91CF9128 +:106090001F910F91FF90EF90DF90CF90BF90AF9046 +:1060A0000895442371F06130710539F020F06230B9 +:1060B000710529F0089528980895299808952A9837 +:1060C00008956130710539F020F06230710529F0D2 +:1060D0000895289A0895299A08952A9A0895613072 +:1060E000710541F020F06230710539F0089595B1E5 +:1060F00081E005C095B182E002C095B184E08927B6 +:1061000085B9089541E00C94513040E00C94513031 +:10611000CF93DF93EC0160E070E00E94853061E096 +:1061200070E0CE010E94853062E070E0CE010E94F6 +:10613000853084B1876084B9DF91CF910895CF9382 +:10614000DF93EC0160E070E00E94853061E070E078 +:10615000CE010E94853062E070E0CE010E94853061 +:1061600084B1887F84B9DF91CF910895CF93DF9375 +:10617000EC01613009F43FC058F1623009F44EC0BF +:10618000633009F064C060E070E00E94823061E03A +:1061900070E0CE010E94823062E070E0CE010E9489 +:1061A000823087EF9BE09F938F930E94674668EB56 +:1061B0007BE080E090E00E949F0A60E070E0CE010A +:1061C0000E94853061E070E0CE010E94853036C0CB +:1061D00060E070E00E94823086E09CE09F938F93A5 +:1061E0000E94674668EB7BE080E090E00E949F0A97 +:1061F00060E070E025C061E070E00E94823080E0E5 +:106200009CE09F938F930E94674668EB7BE080E061 +:1062100090E00E949F0A61E070E012C062E070E0CE +:106220000E9482308BEF9BE09F938F930E94674682 +:1062300068EB7BE080E090E00E949F0A62E070E003 +:10624000CE010E9485300F900F9080E001C08FEF4B +:10625000DF91CF91089547983F9AE8EBF0E02DE069 +:1062600020832CE1208324E6208324E02093BC00BB +:1062700021E0FC0124830895FC01148284E08093D2 +:10628000BC001092B800089585EA9EE00895ECEBFA +:10629000F0E08081806880830895862F86958695BA +:1062A00086958E71982F990F990F890F6F70860FB1 +:1062B00008950F931F93CF93DF938C01EA019E857E +:1062C0009C7F92609E8781E0611101C080E09E8585 +:1062D00080FB97F99E876F87998180FB90F99B7F60 +:1062E00098609983662389F01F929F931F926F9302 +:1062F00089EE9CE09F938F930E9467460F900F90CA +:106300000F900F900F900F9008C088ED9CE09F9326 +:106310008F930E9467460F900F900E5F1F4F21E0F2 +:10632000AE014F5F5F4F61E0C8010E94690888239A +:10633000A1F0AE01425F5F4F22E06EE0C8010E9413 +:106340006908C82F882349F085ED9CE09F938F93BF +:106350000E9467460F900F9009C08FEC9CE09F93BE +:106360008F930E9467460F900F90C0E08C2FDF91B3 +:10637000CF911F910F910895CF92DF92EF92FF92EC +:106380000F931F93CF93DF937C01EA01479B10C0CB +:1063900080EC9CE09F938F930E9467463F9A4798BA +:1063A000AE0160E0C7010E9459310F900F905CC0B0 +:1063B0008EEB9CE09F938F930E9467460F900F9007 +:1063C00009E010E08CE7C82E8CE0D82E1F930F93C5 +:1063D000DF92CF920E94674668EE73E080E090E023 +:1063E0000E94BA0A0F900F900F900F900130110584 +:1063F00031F0015011098F3F2FEF920739F34B977E +:1064000099F1AE016AE0C7010E9459318981886023 +:106410008B7F81608983AE014F5F5F4F21E061E038 +:10642000C70102960E94690888E69CE09F938F93BB +:106430000E9467463F9A479A87E99AE30197F1F7E6 +:1064400000C000003F98479868E873E180E090E062 +:106450000E94BA0A86E39CE09F938F930E9467464E +:106460000F900F900F900F9081E0DF91CF911F91CF +:106470000F91FF90EF90DF90CF9008958F929F92B1 +:10648000AF92BF92CF92DF92EF92FF920F931F9342 +:10649000CF93DF93CDB7DEB727970FB6F894DEBF63 +:1064A0000FBECDBF4C01362FCA011E2DFC2DEA2D8B +:1064B000BA01605D774051E06536710508F450E03F +:1064C0004E8150FB47F94E8364E670E00E94B03E77 +:1064D0006AE00E94963E8295807F982B9F8337FFCB +:1064E00002C0395FFCCF373014F03750FCCF377023 +:1064F0008D81887F382B3D83121614F0245FFCCFEA +:106500002D3014F02C50FCCF822F6AE00E94A23E66 +:106510009F702E81207F922B80FB94F99E83101612 +:1065200014F0015EFCCF003214F00F51FCCF802F2D +:106530006AE00E94A23E9F703C81307C8370282FCD +:106540002295207F832F892B822B8C8317FF02C0FB +:10655000185EFCCF183114F01851FCCF812F6AE07F +:106560000E94A23E9F703B81307C8370282F229531 +:10657000207F832F892B822B8B83F7FF02C0F45C53 +:10658000FCCFFC3314F0FC53FCCF8F2F6AE00E9449 +:10659000A23E9F703A8130788770282F2295207F05 +:1065A000832F892B822B8A83E7FF02C0E45CFCCF18 +:1065B000EC3314F0EC53FCCF8E2F6AE00E94A23E25 +:1065C0009F70398130788770282F2295207F832F04 +:1065D000892B822B8983CE0107969F938F930197F6 +:1065E0009F938F9301979F938F9301979F938F937F +:1065F00001979F938F9301979F938F938E010F5FC6 +:106600001F4F1F930F9384E19CE09F938F930E94F1 +:10661000674678015E0188E0A80EB11C0FB6F894B9 +:10662000DEBF0FBECDBF8EE0C82E8CE0D82EF701A6 +:1066300081917F011F928F93DF92CF920E946746D4 +:106640000F900F900F900F90AE14BF0481F727E0CA +:10665000A80162E0C40102960E94690827960FB65D +:10666000F894DEBF0FBECDBFDF91CF911F910F9188 +:10667000FF90EF90DF90CF90BF90AF909F908F9062 +:1066800008952F923F924F925F926F927F928F92D6 +:106690009F92AF92BF92CF92DF92EF92FF920F93B1 +:1066A0001F93CF93DF93CDB7DEB76E970FB6F894F5 +:1066B000DEBF0FBECDBF611166C22C019C012E5FF3 +:1066C0003F4F3D8F2C8F61E5C9010E94E607CE0147 +:1066D00001969A8F898F80E1E98DFA8D11928A95C2 +:1066E000E9F78FE89EE09F938F930E9467468A8127 +:1066F00081608A8342E0BE016F5F7F4F8C8D9D8DEC +:106700000E94BE080F900F90811103C083EF92E0AA +:1067100002C08EEE92E09F938F930E9467460F9087 +:106720000F9086E89EE09F938F930E9467468EE6C7 +:106730009EE09F938F930E9467468CE49EE09F9318 +:106740008F930E94674686E39EE09F938F930E94FB +:1067500067468CE19EE09F938F930E94674686E098 +:106760009EE09F938F930E9467468FEE9DE09F93DC +:106770008F930E9467468AED9DE09F938F930E94BE +:10678000674684EC9DE09F938F930E9467468CEA56 +:106790009DE09F938F930E94674683E99DE09F93BE +:1067A0008F930E9467460FB6F894DEBF0FBECDBF31 +:1067B000188E84E79DE09F938F930E94674600E1C7 +:1067C0009E012F5F3F4F41E0BE01685E7F4F8C8D81 +:1067D0009D8D0E9406090F900F90811110C083EFCC +:1067E00092E09F938F930E94674668EE73E080E08B +:1067F00090E00E94BA0A4C010F900F90BDC127E0B3 +:1068000030E047E050E0BE016D5F7F4FCE01419622 +:106810000E94BC459E012F5F3F4F790110E0812F00 +:10682000837041F482E79DE09F938F930E946746B7 +:106830000F900F90F70181917F011F928F932CE6AB +:106840003DE03F932F930E9467461F5F0F900F908C +:106850000F900F90103119F78E8987FD03C040ED1E +:1068600057E002C044E358E08F89982F969596959B +:1068700096959E71292F220F220F920F8F70890FEC +:106880001A01280E311C8E89982F9695969596950B +:106890009E71792E770C770C790E8F70780E672C9D +:1068A0008C89982F9695969596959E71B92EBB0CCE +:1068B000BB0CB90E8F70B80EBB8E8B89982F969536 +:1068C000969596959E71D92EDD0CDD0CD90E8F70A4 +:1068D000D80EED2C8A89982F9695969596959E714F +:1068E000F92EFF0CFF0CF90E8F70F80ECF2C898952 +:1068F000182F1695169516951E71912F990F990FB1 +:10690000190F8F70180FA12E0D890E8F073048F4C4 +:10691000E3E00E02C00111249C012255314F4901D0 +:1069200004C039E6832E3DE0932E82E69DE09F93DE +:106930008F930E9467469F928F920E946746812F95 +:10694000012E000C990B9F931F938F2D0F2C000C81 +:10695000990B9F93FF928D2D0D2C000C990B9F93FB +:10696000DF928B2D0B2C000C990B9F93BF92872DE0 +:10697000072C000C990B9F937F923F922F92802FB0 +:10698000002E000C990B9F930F932FE33DE03F9354 +:106990002F930E94674688891F928F93EFE2FDE054 +:1069A000FF93EF930E94674668EE73E080E090E00B +:1069B0000E94BA0A4C010FB6F894DEBF0FBECDBFDD +:1069C00094E68916910409F490C0F4F5FEE48F165C +:1069D000910409F494C08CF438E48316910409F40A +:1069E00087C08DE48816910409F47BC094E489166D +:1069F000910409F0C1C0BA9479C0E7E58E169104FC +:106A000009F485C044F423E58216910409F0B4C06A +:106A1000AA24AA9478C039E58316910409F45AC0CF +:106A200083E68816910409F0A7C08AE0A82E9CE0AE +:106A3000C92E21E1E22E00E128E048EE57E065E0B2 +:106A4000C2010E943E3298C090E789169104D1F1AC +:106A500094F4FDE68F16910409F440C02EE68216E8 +:106A6000910409F449C038E68316910409F084C002 +:106A7000EE24E3943FC084E788169104B1F044F417 +:106A8000E3E78E16910409F077C0AA24A3943BC0D3 +:106A9000F7E78F169104C9F129E78216910409F0EE +:106AA0006BC03FEF231A330A34C08F8587FD02C0C5 +:106AB0006AE001C060E0AE014F5F5F4FC2010E941B +:106AC00059315AC0AE014F5F5F4F6AE0C2010E9468 +:106AD000BC3152C081E0281A31081BC0662463947F +:106AE00002C066246A94670C14C0B394BB8E11C0B4 +:106AF000EE24EA94ED0C0DC0CC24C39402C0CC2447 +:106B0000CA94CF0C06C0A10E04C00F5F01C0015093 +:106B10000E8F8A2D0A2C000C990B9F93AF928C2D0F +:106B20000C2C000C990B9F93CF928E2D0E2C000CE9 +:106B3000990B9F93EF92EB8D8E2F0E2E000C990BDD +:106B40009F93EF93862D062C000C990B9F936F92C9 +:106B50003F922F922AE03DE03F932F930E94674699 +:106B60000B8D262DA1016E8DC2010E943E320FB603 +:106B7000F894DEBF0FBECDBF9BE18916910409F0EA +:106B800017CE80E001C08FEF6E960FB6F894DEBF8F +:106B90000FBECDBFDF91CF911F910F91FF90EF906E +:106BA000DF90CF90BF90AF909F908F907F906F902D +:106BB0005F904F903F902F90089560FF02C0289AF9 +:106BC00001C0289861FF02C0299A01C0299862FF7C +:106BD00002C02A9A01C02A9863FF02C02B9A01C002 +:106BE0002B9864FF02C02C9A01C02C9865FF02C04C +:106BF0002D9A01C02D9866FF02C02E9A01C02E98D2 +:106C000067FF02C02F9A01C02F98809188048130BD +:106C100019F0823061F0089570FF02C05E9A01C0E1 +:106C20005E9871FF02C05F9A08955F98089570FFA3 +:106C300002C0129A01C0129871FF02C0139A0895FF +:106C40001398089580918804813019F08230B1F052 +:106C5000089560FF02C0109A01C0109861FF02C041 +:106C6000119A01C0119862FF02C0129A01C01298D5 +:106C700063FF02C0139A08951398089560FF02C03D +:106C80005C9A01C05C9861FF02C05D9A01C05D988A +:106C900062FF02C05E9A01C05E9863FF02C05F9A05 +:106CA00008955F980895CF93DF93EC0160E070E062 +:106CB0000E94DD3560E0CE010E9422368FEF84B95C +:106CC00080918804813041F0823061F481B18E601E +:106CD00081B98AB1806F05C081B18F6081B98AB1F5 +:106CE000806E8AB9DF91CF910895CF93DF93EC0145 +:106CF00060E070E00E94DD3560E0CE010E94223647 +:106D000014B880918804813041F0823061F481B1FF +:106D1000817F81B98AB18F7005C081B1807F81B9CF +:106D20008AB18F718AB9DF91CF91089580918804DB +:106D3000813019F0823039F00895662311F05D98A2 +:106D400008955D9A0895662311F011980895119A97 +:106D50000895CF92DF92EF92FF920F931F93CF93FC +:106D6000DF937C01611117C06FE00E9422366FEF44 +:106D700073E0C7010E94DD3561E0C7010E949636CD +:106D80008FEE9EE09F938F930E94674660ED77E0C1 +:106D900080E090E028C0613069F48BEE9EE09F9324 +:106DA0008F930E94674668EE73E080E090E00E9457 +:106DB0009F0A20C0623039F460E071E00E94DD3546 +:106DC00082EE9EE008C06330C1F460E072E00E9491 +:106DD000DD3589ED9EE09F938F930E94674668EEB4 +:106DE00073E080E090E00E949F0A60E070E0C701DD +:106DF0000E94DD350F900F9044C0643208F043C00C +:106E0000C62FD0E024978E0183E0159507958A95CB +:106E1000E1F7C770DD27CC24C394D12CB601002E36 +:106E200001C0660F0A94EAF7C7010E9422360C2EB1 +:106E300002C0CC0CDD1C0A94E2F7B601C7010E9427 +:106E4000DD35DF92CF92CC0FDD1FC05EDE4F898132 +:106E50008F9388818F931F930F9384EC9EE09F9371 +:106E60008F930E94674660E971E080E090E00E94A5 +:106E70009F0A8DB79EB708960FB6F8949EBF0FBEB7 +:106E80008DBF80E001C08FEFDF91CF911F910F91F7 +:106E9000FF90EF90DF90CF90089581B1807F81B90E +:106EA00082B18F6082B9089582B1807F82B981B149 +:106EB000807F81B908956130710581F058F06230AA +:106EC000710581F063307105A1F480B183FB8827DF +:106ED00080F90CC080B180958170089580B186954D +:106EE000817004C080B182FB882780F991E08927F6 +:106EF000089580E008950F931F93CF93DF9360313F +:106F000008F04EC08C01C62FC695C695D0E06370C0 +:106F1000613021F0633009F44BC021C0BE010E94F2 +:106F20005B37811145C0CE0101969F938F9385E019 +:106F30009FE09F938F930E9467460F900F900F9052 +:106F40000F90BE01C8010E945B3781112BC060E029 +:106F500070E0CB010E949F0A019621F5F2CFBE019D +:106F60000E945B37882321F1CE0101969F938F9376 +:106F700087EF9EE09F938F930E9467460F900F903C +:106F80000F900F90BE01C8010E945B37882351F01B +:106F900060E070E0CB010E949F0A019619F4F2CFE5 +:106FA0008FEF07C06AE070E080E090E00E949F0AE7 +:106FB00080E0DF91CF911F910F910895CF93C82F5B +:106FC0008A3019F48DE00E94DE378091C80085FF79 +:106FD000FCCFC093CE0080E090E0CF9108955A9A04 +:106FE00022E02093C80028E92093C90026E02093DE +:106FF000CA001092CD002CE02093CC0020E331E0B9 +:107000003093E7042093E60421E0FC0122830895F5 +:10701000FC0112821092C8001092C9001092CA009E +:107020001092CD001092CC001092E7041092E6046A +:1070300008950F931F93CF93DF93611122C007E34D +:107040001FE0C7E1DFE01F930F930E946746DF93C5 +:10705000CF938091E7048F938091E6048F930E94F1 +:10706000104668E873E180E090E00E949F0A0F906C +:107070000F900F900F900F900F90019621F380E0EA +:1070800001C08FEFDF91CF911F910F910895862F4F +:107090006091E6047091E7040C94DE37A0E1B0E063 +:1070A000E4E5F8E30C94EF3F7C011B016A01FC016D +:1070B00017821682838181FF44C39E012F5F3F4F59 +:1070C0003901F7019381F10193FD859193FF81913E +:1070D0001F01882309F431C3853239F493FD85916A +:1070E00093FF81911F01853239F4B70190E00E942E +:1070F0002B4656016501E5CF10E0512C912CFFE1A4 +:10710000F915D8F08B3279F038F4803279F0833287 +:10711000A1F4F92DF0612EC08D3261F0803369F455 +:10712000292D21602DC0392D3260932E892D846048 +:10713000982E2AC0E92DE86015C097FC2DC020EDDF +:10714000280F2A3088F496FE06C03AE0139F200DDF +:107150001124122F19C08AE0589E200D1124522E9E +:10716000E92DE0629E2E10C08E3231F496FCE5C20D +:10717000F92DF0649F2E08C08C3621F4292D20684B +:10718000922E02C0883641F4F10193FD859193FF60 +:1071900081911F018111B3CF9BEB980F933020F4A5 +:1071A000992D9061805E07C09BE9980F933008F09D +:1071B00066C1992D9F7E96FF16E09F73992E8536A6 +:1071C00019F49064992E08C0863621F4392F30685E +:1071D000932E02C01111115097FE07C01C3350F4BA +:1071E00044244394410E27E00BC0183038F027E0C8 +:1071F00017E005C027E09CE3492E02C0212F412C57 +:10720000560184E0A80EB11CF60160817181828173 +:107210009381042DA3010E94D0446C01F981FC8765 +:10722000F0FF02C0F3FF06C091FC06C092FE06C04C +:1072300000E205C00DE203C00BE201C000E08C8556 +:107240008C7019F001115AC29BC297FE10C04C0CF1 +:10725000FC85F4FF04C08A81813309F44A94141434 +:1072600074F528E0241578F588E0482E2CC096FCAB +:107270002AC0812F90E08C159D059CF03CEFC31631 +:107280003FEFD30674F0892D8068982E0AC0E2E0A3 +:10729000F0E0EC0FFD1FE10FF11D8081803319F448 +:1072A00011501111F4CF97FE0EC044244394410EA7 +:1072B000812F90E0C816D9062CF41C1904C0442470 +:1072C000439401C010E097FE06C01C141D0434F462 +:1072D000C601019605C085E090E002C081E090E023 +:1072E00001110196112331F0212F30E02F5F3F4F24 +:1072F000820F931F252D30E08217930714F4581A3C +:1073000001C0512C892D897049F4552039F0B701FD +:1073100080E290E00E942B465A94F7CF002329F098 +:10732000B701802F90E00E942B4693FC09C05520A6 +:1073300039F0B70180E390E00E942B465A94F7CFD2 +:1073400097FE4CC04601D7FE02C0812C912CC6018D +:1073500088199909F301E80FF91FFE87ED87960157 +:1073600024193109388B2F87012F10E011950195D1 +:1073700011093FEF8316930629F4B7018EE290E0DE +:107380000E942B46C814D9044CF08F859889881523 +:10739000990524F4ED85FE85818101C080E3F1E04B +:1073A0008F1A91082D853E852F5F3F4F3E872D8791 +:1073B000801691062CF0B70190E00E942B46D9CFA1 +:1073C000C814D90441F49A81963320F4953319F402 +:1073D0003C8534FF81E3B70190E04EC08A81813360 +:1073E00019F09C859F7E9C87B70190E00E942B46F8 +:1073F000111105C094FC18C085E690E017C0B701D4 +:107400008EE290E00E942B461E5F82E001E0080FB2 +:10741000F301E80FF11D8081B70190E00E942B4637 +:10742000802F0113F3CFE6CF85E490E0B7010E94EF +:107430002B46D7FC06C0C114D10441F4EC85E4FF0F +:1074400005C0D194C194D1088DE201C08BE2B7018F +:1074500090E00E942B4680E32AE0C216D1042CF073 +:107460008F5FFAE0CF1AD108F7CFB70190E00E9402 +:107470002B46B701C601C0960E942B4654C18336E5 +:1074800031F0833779F0833509F056C020C05601BA +:1074900032E0A30EB11CF6018081898301E010E087 +:1074A000630112C05601F2E0AF0EB11CF601C080BC +:1074B000D18096FE03C0612F70E002C06FEF7FEFB6 +:1074C000C6010E94D9458C01F92DFF7714C05601E1 +:1074D00022E0A20EB11CF601C080D18096FE03C04E +:1074E000612F70E002C06FEF7FEFC6010E94B145CF +:1074F0008C01F92DF0689F2EF3FD1AC0852D90E0C8 +:1075000008171907A8F4B70180E290E00E942B4603 +:107510005A94F4CFF60197FC859197FE81916F0103 +:10752000B70190E00E942B4651105A940150110966 +:107530000115110579F7F7C0843611F0893661F528 +:10754000560197FE09C024E0A20EB11CF60160812D +:107550007181828193810AC0F2E0AF0EB11CF60105 +:1075600060817181072E000C880B990BF92DFF7635 +:107570009F2E97FF09C090958095709561957F4FDC +:107580008F4F9F4FF0689F2E2AE030E0A3010E94AA +:10759000A646C82EC6183FC0092D853721F40F7E98 +:1075A0002AE030E01DC0097F8F3691F018F488354D +:1075B00059F0C3C0803719F0883711F0BEC00061A0 +:1075C00004FF09C0046007C094FE08C0066006C03E +:1075D00028E030E005C020E130E002C020E132E0E8 +:1075E000560107FF09C084E0A80EB11CF6016081B6 +:1075F00071818281938108C0F2E0AF0EB11CF60167 +:107600006081718180E090E0A3010E94A646C82EAF +:10761000C6180F77902E96FE0BC0092D0E7FC1164F +:1076200050F494FE0AC092FC08C0092D0E7E05C0DD +:10763000DC2C092D03C0DC2C01C0D12E04FF0DC0B1 +:10764000FE01EC0DF11D8081803311F4097E09C02B +:1076500002FF06C0D394D39404C0802F867809F02B +:10766000D39403FD11C000FF06C01C2DD51480F477 +:10767000150D1D190DC0D51458F4B70180E290E026 +:107680000E942B46D394F7CFD51410F45D1801C097 +:10769000512C04FF10C0B70180E390E00E942B46FC +:1076A00002FF17C001FD03C088E790E002C088E533 +:1076B00090E0B7010CC0802F867859F001FF02C01E +:1076C0008BE201C080E207FD8DE2B70190E00E94ED +:1076D0002B46C11638F4B70180E390E00E942B4698 +:1076E0001150F7CFCA94F301EC0DF11D8081B70161 +:1076F00090E00E942B46C110F5CF15C0F4E0F515BF +:1077000060F584E0581A93FE1FC0011127C02C8534 +:1077100023FF2AC00EEF10E0392D3071932EF801AF +:107720008491811124C0552009F4E4CCB70180E292 +:1077300090E00E942B465A94F6CFF70186819781FC +:1077400026C08FEF9FEF23C0B70180E290E00E9438 +:107750002B465A945110F8CFD8CF512CB701802F17 +:1077600090E00E942B46D3CF02E011E0D5CF9110DC +:107770008052B70190E00E942B460F5F1F4FCFCF82 +:1077800023E0251510F483E0BDCF512CC0CF6096C7 +:10779000E2E10C940B400F931F93CF93DF93EC0126 +:1077A0008B0180EA91E099838883CE0104960E9440 +:1077B000AB0CCE01CD960E94F415CE0187589F4F99 +:1077C0000E94DB07CE0185589F4F0E944109FE01B0 +:1077D000E057FF4F10821B830A83DF91CF911F91E7 +:1077E0000F910895CF93DF93CDB7DEB7AE970FB665 +:1077F000F894DEBF0FBECDBF87B18C6987B988B161 +:10780000837688B93D98459A82E08093C00098E9D4 +:107810009093C10096E09093C2001092C5009CE046 +:107820009093C4008093B0008093B10096E9909348 +:10783000B3008093700080E191E09093E504809321 +:10784000E40482E091E09093E3048093E20478946E +:107850000E94420A86E2EEE3F1E0DE011196019019 +:107860000D928A95E1F780918804811102C087E02A +:1078700001C083E1F9EFCF2EF2E0DF2EABEFAA2EAD +:10788000A2E0BA2E682E712C4E0127E2820E911CC6 +:10789000CE018B969EA78DA762E7262E62E0362E3C +:1078A00078E3472E73E0572EEEE6EE2EE2E0FE2E52 +:1078B000DF92CF920E946746BF92AF920E946746C6 +:1078C000A4E2B3E0BF93AF930E9467463F922F922A +:1078D0000E9467465F924F920E946746FF92EF9226 +:1078E0000E9467468FE293E09F938F930E946746C2 +:1078F000BF92AF920E946746809188049FEF980FD5 +:107900000FB6F894DEBF0FBECDBF923078F40E9460 +:10791000350A9F938F938EE592E09F938F930E94F9 +:1079200067460F900F900F900F9008C086E492E08A +:107930009F938F930E9467460F900F90BF92AF92D4 +:107940000E946746DF92CF920E94674683E392E0EF +:107950009F938F930E9467461CA61BA60F900F90C3 +:107960000F900F900F900F908BA59CA586159705F3 +:1079700070F5FC01EE0FFF1F21E030E02C0F3D1FE2 +:10798000E20FF31F008111819F938F938AE292E0AF +:107990009F938F930E946746D801ED91FC910680DA +:1079A000F781E02DC80109959F938F930E94674648 +:1079B000DF92CF920E9467468BA59CA501969CA75B +:1079C0008BA70FB6F894DEBF0FBECDBFCDCF8BE136 +:1079D00092E09F938F930E94674610925F041092EB +:1079E0005E044091E2045091E30464E070E0C4015D +:1079F0000E94E4453EA53F938DA58F9388E791E0D3 +:107A00009F938F939F928F920E948D460FB6F8940A +:107A1000DEBF0FBECDBF019709F04ACF8BA59CA555 +:107A20008615970508F044CF880F991FE1E0F0E034 +:107A3000EC0FFD1FE80FF91F0081118187E192E033 +:107A40009F938F930E946746D801ED91FC91068029 +:107A5000F781E02DC80109959F938F930E94674697 +:107A600083E192E09F938F930E9467468FEF9FEF91 +:107A70009093010180930001D801ED91FC91028067 +:107A8000F381E02DC80109950F900F900F900F9092 +:107A90000F900F90E12CF12C3CE0432E32E0532E5E +:107AA00022242A94322CFF92EF925F924F920E94EE +:107AB0006746D801ED91FC910190F081E02D6E2D8B +:107AC000C80109950F900F900F900F9087FD18C077 +:107AD00080910001909101014B9739F48FEF9FEF56 +:107AE00090930101809300010BC03092010120921C +:107AF0000001BFEFEB1AFB0A2FEFE216F10499F633 +:107B0000D801ED91FC910480F581E02DC801099523 +:107B1000C3CE84EC91E090935D0480935C0481E497 +:107B200092E090935B0480935A048CE092E09093EF +:107B300059048093580484E292E090935604809311 +:107B400055041092570484EF91E09093540480936D +:107B5000530482E991E09093500480934F04109273 +:107B600052041092510480E092E090934E0480936E +:107B70004D048CED91E09093490480934804109259 +:107B80004A0410924C0410924B0488EE91E09093BA +:107B900047048093460488EB91E0909343048093DC +:107BA000420481E090918804913009F480E0809350 +:107BB0004404109245048DE492E090934004809335 +:107BC0003F041092410480ED91E090932C04809347 +:107BD0002B0410922D0410922E048CEA91E09093C5 +:107BE000270480932604109228048FEF9FEF909330 +:107BF0002A048093290460E070E085E993E00E9404 +:107C0000CB3B61E070E084E093E00E94CB3B62E01C +:107C100070E083E792E00E94CB3B88E192E0909392 +:107C20006D0280936C028EE692E00E94DB07109258 +:107C30007002109272021092710286E891E09093A5 +:107C400062028093610210926A0221E030E0309378 +:107C500069022093680210926302109264021092EB +:107C60006502109266021092670290935702809309 +:107C7000560210925F0282E090E090935E02809341 +:107C80005D02109258021092590210925A021092FC +:107C90005B0210925C0208958AE994E00E94AB0CAA +:107CA00088E191E0909399048093980490939704CD +:107CB00080939604909395048093940408958AE3A6 +:107CC00097E008958EE199E0089585E599E008959B +:107CD00086E699E0089587E49AE0089585ED9AE0B4 +:107CE00008958EE79BE008958AEA9BE0089583EF6C +:107CF0009BE008958AE09CE0089582EF9EE008955D +:107D000080E19FE0089587E59FE00895DB018F9370 +:107D10009F930E94E33EBF91AF91A29F800D911D62 +:107D2000A39F900DB29F900D11240895991B79E0A7 +:107D300004C0991F961708F0961B881F7A95C9F7FB +:107D40008095089587FB082E062687FD819567FD9F +:107D500061950E94963E0EF4919507FC81950895D9 +:107D6000AA1BBB1B51E107C0AA1FBB1FA617B70761 +:107D700010F0A61BB70B881F991F5A95A9F780957D +:107D80009095BC01CD010895052E97FB1EF400943B +:107D90000E94DB3E57FD07D00E94874007FC03D0BE +:107DA0004EF40C94DB3E50954095309521953F4F15 +:107DB0004F4F5F4F089590958095709561957F4FD7 +:107DC0008F4F9F4F08950E94AF40A59F900DB49F85 +:107DD000900DA49F800D911D11240895B7FF0C9460 +:107DE000E33E0E94E33E821B930B0895DF93CF9303 +:107DF0001F930F939A9DF02D219FF00D8B9DF00DF9 +:107E00008A9DE02DF10D039FF00D029FE00DF11D05 +:107E10004E9DE00DF11D5E9DF00D4F9DF00D7F9389 +:107E20006F93BF92AF925F934F93D5010E94AF4083 +:107E30008B01AC01D7010E94AF40EB01E80FF91FA5 +:107E4000D6010E94463F2F913F91D6010E94AF403C +:107E5000C60FD71FE81FF91FAF91BF910E94463F81 +:107E60002F913F910E94AF40C60FD71FE81FF91F07 +:107E7000D6010E94AF40E60FF71F9801BE01CF0167 +:107E800011240F911F91CF91DF9108950E94AF406F +:107E9000460F571FC81FD91F08F4319608956894DC +:107EA00001C0E894F92FF12B12F00C94833FA0E06D +:107EB000B0E0EDE5FFE30C94F53F092E059422F4C4 +:107EC0000E94DF3F112392F4F0E80F26FFEFE094C9 +:107ED000F09400951095B094C094D094A194BF0AEA +:107EE000CF0ADF0AEF0AFF0A0F0B1F0B0E948E3F1B +:107EF00007FC0E94DF3FCDB7DEB7ECE00C941140E9 +:107F0000689401C0E8948F929F92CF93DF930E9470 +:107F10008E3FDF91CF919F908F9008958824992470 +:107F2000F401E401B0E49F93AA279A158B049C0402 +:107F3000ED05FE05CF05D007A10798F4AD2FDC2F86 +:107F4000CF2FFE2FE92D982C892E982F872F762F53 +:107F5000652F542F432F322F2227B85031F7BF916E +:107F600027C01B2EBF91BB27220F331F441F551F55 +:107F7000661F771F881F991F881C991CEE1FFF1F03 +:107F8000CC1FDD1FAA1FBB1F8A149B04EC05FD0537 +:107F9000CE05DF05A007B10748F08A189B08EC0959 +:107FA000FD09CE09DF09A00BB10B21601A94E1F69F +:107FB0002EF49401AF01BE01CD01000C089560952F +:107FC00070958095909530954095509521953F4FAF +:107FD0004F4F5F4F6F4F7F4F8F4F9F4F08952F929F +:107FE0003F924F925F926F927F928F929F92AF9249 +:107FF000BF92CF92DF92EF92FF920F931F93CF9396 +:10800000DF93CDB7DEB7CA1BDB0B0FB6F894DEBF2C +:108010000FBECDBF09942A88398848885F846E8452 +:108020007D848C849B84AA84B984C884DF80EE809C +:10803000FD800C811B81AA81B981CE0FD11D0FB6A5 +:10804000F894DEBF0FBECDBFED0108950F93083049 +:1080500090F0982F872F762F652F542F432F322F94 +:1080600022270850F4CF220F331F441F551F661FCD +:10807000771F881F991F0A95B2F70F91089597FBF4 +:1080800010F8169400080F93083098F00850232F2A +:10809000342F452F562F672F782F892F902DF4CF0F +:1080A0000594979587957795679557954795379553 +:1080B00027950A95AAF70F9108952A0D3B1D4C1D8F +:1080C0005D1D6E1D7F1D801F911F08950024A7FD5B +:1080D00000942A0F301D401D501D601D701D801D15 +:1080E000901D08952A193B094C095D096E097F0905 +:1080F000800B910B08950024A7FD00942A173005EA +:108100004005500560057005800590050895A1E2C1 +:108110001A2EAA1BBB1BFD010DC0AA1FBB1FEE1F01 +:10812000FF1FA217B307E407F50720F0A21BB30B4C +:10813000E40BF50B661F771F881F991F1A9469F7C8 +:1081400060957095809590959B01AC01BD01CF0124 +:108150000895EE0FFF1F0590F491E02D0994A29F62 +:10816000B001B39FC001A39F700D811D1124911D0B +:10817000B29F700D811D1124911D08955058BB2789 +:10818000AA270E94D6400C947B420E946D4238F090 +:108190000E94744220F039F49F3F19F426F40C94A5 +:1081A0005A420EF4E095E7FB0C945442E92F0E94EA +:1081B0008C4258F3BA17620773078407950720F0BB +:1081C00079F4A6F50C94AE420EF4E0950B2EBA2F7E +:1081D000A02D0B01B90190010C01CA01A0011124CD +:1081E000FF27591B99F0593F50F4503E68F11A1679 +:1081F000F040A22F232F342F4427585FF3CF46950A +:1082000037952795A795F0405395C9F77EF41F162B +:10821000BA0B620B730B840BBAF09150A1F0FF0FF5 +:10822000BB1F661F771F881FC2F70EC0BA0F621FE1 +:10823000731F841F48F4879577956795B795F795D1 +:108240009E3F08F0B0CF9395880F08F09927EE0F66 +:108250009795879508950E943F410C947B420E9418 +:10826000744258F00E946D4240F029F45F3F29F0BB +:108270000C94544251110C94AF420C945A420E94F7 +:108280008C4268F39923B1F3552391F3951B550B59 +:10829000BB27AA2762177307840738F09F5F5F4FD9 +:1082A000220F331F441FAA1FA9F335D00E2E3AF018 +:1082B000E0E832D091505040E695001CCAF72BD030 +:1082C000FE2F29D0660F771F881FBB1F2617370781 +:1082D0004807AB07B0E809F0BB0B802DBF01FF27B3 +:1082E00093585F4F3AF09E3F510578F00C945442FA +:1082F0000C94AF425F3FE4F3983ED4F386957795B4 +:108300006795B795F7959F5FC9F7880F911D96956B +:10831000879597F90895E1E0660F771F881FBB1FC7 +:10832000621773078407BA0720F0621B730B840B74 +:10833000BA0BEE1F88F7E09508950E94A441689457 +:10834000B1110C94AF4208950E94944288F09F5757 +:1083500098F0B92F9927B751B0F0E1F0660F771F69 +:10836000881F991F1AF0BA95C9F714C0B13091F05F +:108370000E94AE42B1E008950C94AE42672F782F70 +:108380008827B85F39F0B93FCCF386957795679524 +:10839000B395D9F73EF490958095709561957F4F90 +:1083A0008F4F9F4F089597FB16F40E945D420C94E7 +:1083B0001742E89409C097FB3EF49095809570951C +:1083C00061957F4F8F4F9F4F9923A9F0F92F96E921 +:1083D000BB279395F695879577956795B795F11196 +:1083E000F8CFFAF4BB0F11F460FF1BC06F5F7F4F33 +:1083F0008F4F9F4F16C0882311F096E911C0772345 +:1084000021F09EE8872F762F05C0662371F096E84D +:10841000862F70E060E02AF09A95660F771F881F1C +:10842000DAF7880F9695879597F90895E894F92FCC +:1084300096EBFF2381F0121613061406440B939556 +:10844000F69587957795679557954040FF23B9F73F +:108450001BC099270895882351F49850D2F7872B91 +:10846000762F652F542F432F322F20E0B1F31216B1 +:1084700013061406440B88233AF09A95440F551FAF +:10848000661F771F881FCAF755234AF4440F551FEC +:1084900011F460FF04C06F5F7F4F8F4F9F4F880FB5 +:1084A0009695879597F9089597F99F6780E870E00A +:1084B00060E008959FEF80EC089590958095709509 +:1084C000609550954095309521953F4F4F4F5F4FA8 +:1084D0006F4F7F4F8F4F9F4F089500240A941616B9 +:1084E000170618060906089500240A9412161306A2 +:1084F000140605060895092E0394000C11F4882330 +:1085000052F0BB0F40F4BF2B11F460FF04C06F5F4B +:108510007F4F8F4F9F4F089557FD9058440F551F21 +:1085200059F05F3F71F04795880F97FB991F61F0F5 +:108530009F3F79F087950895121613061406551F6C +:10854000F2CF4695F1DF08C0161617061806991FD8 +:10855000F1CF86957105610508940895E894BB27CD +:1085600066277727CB0197F908950E94C8420C949B +:108570007B420E946D4238F00E94744220F09523A5 +:1085800011F00C9454420C945A4211240C94AF42B2 +:108590000E948C4270F3959FC1F3950F50E0551FD8 +:1085A000629FF001729FBB27F00DB11D639FAA2748 +:1085B000F00DB11DAA1F649F6627B00DA11D661F97 +:1085C000829F2227B00DA11D621F739FB00DA11DB8 +:1085D000621F839FA00D611D221F749F3327A00D72 +:1085E000611D231F849F600D211D822F762F6A2F0E +:1085F00011249F5750409AF0F1F088234AF0EE0F73 +:10860000FF1FBB1F661F771F881F91505040A9F79F +:108610009E3F510580F00C9454420C94AF425F3F52 +:10862000E4F3983ED4F3869577956795B795F795DB +:10863000E7959F5FC1F7FE2B880F911D9695879553 +:1086400097F90895FA01EE0FFF1F309621053105C5 +:10865000A1F16115710561F48038BFE39B0749F111 +:1086600068949038810561F08038BFEF9B0741F036 +:1086700099234AF5FF3FE1053105210519F1E894F9 +:108680000894E795D901AA2329F4AB2FBE2FF85FF0 +:10869000D0F310C0FF5F70F4A695E0F7F73950F003 +:1086A00019F0FF3A38F49F779F930DD00F9007FC95 +:1086B0009058089546F00C945A4260E070E080E8CB +:1086C0009FE308954FE79F775F934F933F932F93D7 +:1086D0000E9446442F913F914F915F910E94B54275 +:1086E0000C947F430E94B143880B990B089529F4A1 +:1086F00016F00C9454420C94AE420C945A420E94D0 +:108700009442A8F39638A0F707F80F92E8942BE369 +:108710003AEA48EB5FE70E94CB420F920F920F922A +:108720004DB75EB70F920E948E44ECE7F0E00E94D6 +:10873000D5434F915F91EF91FF91E595EE1FFF1F9C +:1087400049F0FE57E0684427EE0F441FFA95E1F721 +:108750004195550B0E9408440F9007FE0C94FC4372 +:108760000895990F0008550FAA0BE0E8FEEF1616C2 +:108770001706E807F907C0F012161306E407F50715 +:1087800098F0621B730B840B950B39F40A2661F089 +:10879000232B242B252B21F408950A2609F4A1402C +:1087A000A6958FEF811D811D0895DF93CF931F93B1 +:1087B0000F93FF92EF92DF927B018C01689406C0C9 +:1087C000DA2EEF010E94C842FE01E894A59125919E +:1087D000359145915591A6F3EF010E94D640FE01D7 +:1087E0009701A801DA9469F7DF90EF90FF900F915D +:1087F0001F91CF91DF9108959B01AC0160E070E083 +:1088000080E89FE30C942B410C9454420C94C24496 +:108810000E949442D8F39923C9F3940F511DA3F3F6 +:108820009150504094F059F0882332F0660F771F32 +:10883000881F91505040C1F79E3F51052CF7880F7B +:10884000911D9695879597F908955F3FACF0983EF6 +:108850009CF0BB27869577956795B79508F4B1602E +:108860009395C1F7BB0F58F711F460FFE8CF6F5F26 +:108870007F4F8F4F9F4FE3CF0C94AF4216F00C9475 +:10888000C2440C945A4268940C9454420E949442FC +:10889000A8F39923C1F3AEF3DF93CF931F930F9304 +:1088A000FF92C92FDD2788232AF02197660F771FB3 +:1088B000881FDAF720E030E040E85FEB9FE388397B +:1088C00020F0803E38F021968F770E94BF40E4EA86 +:1088D000F0E004C00E94BF40E1EDF0E00E94D5430B +:1088E0008B01BE01EC01FB2E6F5771097595771F47 +:1088F000880B990B0E94DB4128E132E741E35FE3FB +:108900000E94C842AF2D9801AE01FF900F911F91B8 +:10891000CF91DF910E94D6400C947B42FA01DC019A +:10892000AA0FBB1F9B01AC01BF5728F42227332796 +:108930004427507820C0B75190F4AB2F00244695BF +:1089400037952795011CA395D2F3002071F0220FD3 +:10895000331F441FB395DAF30ED00C94BE40613040 +:108960007105A0E88A07B94630F49B01AC0166277F +:10897000772788279078309621F0208331834283AF +:10898000538308959F3F31F0915020F48795779558 +:108990006795B795880F911D9695879597F90895D6 +:1089A000283008F027E03327DA01990F311D87FDC1 +:1089B000916000966105710539F432602E5F3D9338 +:1089C00030E32A95E1F708959F3F30F08038710534 +:1089D000610509F03C5F3C5F3D93913008F0806891 +:1089E000911DDF93CF931F930F93FF92EF92192F57 +:1089F000987F9695E92F96959695E90FFF27E05A6F +:108A0000FE4F99273327EE24FF24A701E7010590A5 +:108A10000894079428F4360FE71EF81E491F511DCD +:108A2000660F771F881F991F0694A1F7059007947A +:108A300028F4E70EF81E491F561FC11D770F881F27 +:108A4000991F661F0694A1F70590079428F4F80E65 +:108A5000491F561FC71FD11D880F991F661F771FFB +:108A60000694A1F70590079420F4490F561FC71FDD +:108A7000D81F990F661F771F881F0694A9F7849146 +:108A80001095177041F0D695C79557954795F7946F +:108A9000E7941A95C1F7E6E0F1E068941590159116 +:108AA00035916591959105907FE27395E118F10AF2 +:108AB000430B560BC90BD009C0F7E10CF11E431F45 +:108AC000561FC91FD01D7EF4703311F48A95E6CF6E +:108AD000E894015030F0080F0AF40027021708F458 +:108AE000202F2395022F7A3328F079E37D932A955E +:108AF000E9F710C07D932A9589F6069497956795B6 +:108B0000379517951794E118F10A430B560BC90BCB +:108B1000D00998F023957E9173957A3308F070E32D +:108B20007C932013B8F77E9170617D9330F083952C +:108B300071E37D9370E32A95E1F71124EF90FF90A4 +:108B40000F911F91CF91DF91992787FD90950895FF +:108B5000FB01DC0102C005900D9241505040D8F756 +:108B60000895FC010590615070400110D8F7809580 +:108B700090958E0F9F1F0895FB01DC01215030401E +:108B800030F001900D920416C9F7CD0108958827A1 +:108B900099270895FB01DC014150504048F00190B5 +:108BA0000D920020C9F701C01D9241505040E0F7DE +:108BB0000895FC016150704001900110D8F7809534 +:108BC00090958E0F9F1F0895A0E0B0E0EAEEF5E4C7 +:108BD0000C94F93FFA01238120FD03C080E090E06E +:108BE0001AC016161706D4F77A018C01EB016C0136 +:108BF000C130D10569F0C7010E94C5498F3FFFEF21 +:108C00009F0761F3F60181936F0121970A9781F71E +:108C1000F6011082C801CDB7DEB7E8E00C9415402C +:108C2000A0E0B0E0E6E1F6E40C94FD3F0F8118858A +:108C3000F801838188608383AE01455F5F4F69855A +:108C40007A85C8010E944E38F8012381277F23834B +:108C5000E4E00C9419400F931F93CF93DF93FB0133 +:108C6000238121FD03C08FEF9FEF2CC022FF16C090 +:108C700046815781248135814217530744F4A081EE +:108C8000B1819D012F5F3F4F318320838C932681DB +:108C900037812F5F3F4F3783268314C08B01EC0150 +:108CA000FB010084F185E02D0995892BE1F6D801BF +:108CB00016968D919C911797019617969C938E937B +:108CC0001697CE01DF91CF911F910F910895A0E0EB +:108CD000B0E0EDE6F6E40C94FD3FAE01495F5F4F76 +:108CE000DA016D917D91AD0102EE14E0F80182810F +:108CF0009381DC0113962C911397286013962C9383 +:108D00000E944E38D8011296ED91FC911397238161 +:108D1000277F2383E4E00C941940AEE0B0E0E3E960 +:108D2000F6E40C94FF3F85E08C838B899C899A83C1 +:108D30008983AE01495E5F4F6D897E89CE010196C0 +:108D40000E94A0482E96E2E00C941B40FA01AA274C +:108D5000283051F1203181F1E8946F936E7F6E5F7E +:108D60007F4F8F4F9F4FAF4FB1E03ED0B4E03CD02C +:108D7000670F781F891F9A1FA11D680F791F8A1F0F +:108D8000911DA11D6A0F711D811D911DA11D20D076 +:108D900009F468943F912AE0269F11243019305D30 +:108DA0003193DEF6CF010895462F4770405D419321 +:108DB000B3E00FD0C9F7F6CF462F4F70405D4A336E +:108DC00018F0495D31FD4052419302D0A9F7EACF36 +:108DD000B4E0A6959795879577956795BA95C9F765 +:108DE00000976105710508959B01AC010A2E069458 +:108DF0005795479537952795BA95C9F7620F731F11 +:108E0000841F951FA01D089520FD09C0FC0123FDAE +:108E100005C022FF02C0738362835183408308959B +:108E200044FD17C046FD17C0AB01BC01DA01FB01D0 +:108E3000AA0FBB1FEE1FFF1F1094D1F74A0F5B1F35 +:108E40006E1F7F1FCB01BA01660F771F881F991F06 +:108E500009C033E001C034E0660F771F881F991FF7 +:108E60003150D1F7620F711D811D911D08950F932F +:108E70001F93CF93DF938C01C8010E94C549EC0179 +:108E800097FD08C00E94B149892BB1F7B801CE0106 +:108E90000E94034ACE01DF91CF911F910F91089557 +:108EA0008F929F92AF92BF92EF92FF920F931F9378 +:108EB000CF93DF938C01D62F7A01B22E0E94C54941 +:108EC0009C0133272B32310531F02D32310561F40D +:108ED0008B2D8068B82ED15011F480E068C0C80195 +:108EE0000E94C54997FDF9CFCB2DCD7F2B2D207347 +:108EF00009F58033F9F4AA24AA94AD0E09F443C00D +:108F0000C8010E94C54997FD3EC09C012F7D3327B3 +:108F10002835310549F4C264D250A9F1C8010E9434 +:108F2000C54997FF07C02FC0B6FE02C0C26001C08E +:108F3000C261DA2D812C912C540120ED280F2830AC +:108F400080F0C4FF04C0B8010E94034A19C02A304F +:108F500040F0C6FFF8CF2F7D3FEE320F363098F746 +:108F600027504C2FC501B4010E9410474B015C01F2 +:108F7000C260D15059F0C8010E94C54997FFDDCFAA +:108F8000C1FD04C0AACF812C912C5401C7FF08C099 +:108F9000B094A09490948094811C911CA11CB11C4D +:108FA0002C2FB501A401C7010E94044781E0DF9185 +:108FB000CF911F910F91FF90EF90BF90AF909F9036 +:108FC0008F900895A0E2B0E0E8EEF7E40C94F23F51 +:108FD0005C01962E7A01F9018E010F5F1F4F680127 +:108FE00080E2D8011D928A95E9F7D50113968C90FD +:108FF00080E090E0612C712C30E061E070E083FC57 +:10900000259183FE21918F01522E211103C080E012 +:1090100090E092C02E3511F4009751F1432F50E0AB +:10902000481759073CF42D3559F12D3219F47720A2 +:1090300009F103C0772009F46AC0452D469546958D +:109040004695D601A40FB11D452D47708B0102C076 +:10905000000F111F4A95E2F7A8015C91452B4C9334 +:10906000651459F0561410F45394E7CF5A94E5CF91 +:1090700031E004C07724739401C0712C0196BFCFF6 +:10908000772019F08E8180628E83311103C088248D +:10909000839417C0F6019E012F5D3F4F808180951C +:1090A00081932E173F07D1F7F2CFE114F10429F095 +:1090B000D7018C93F70131967F019A94812C9920E6 +:1090C000F9F0C5010E94C54997FD18C0FC01FF27B2 +:1090D00023E0F595E7952A95E1F7EC0DFD1D20813C +:1090E00030E0AC014770552702C0359527954A9569 +:1090F000E2F720FDDACFB5010E94034A811087CF45 +:10910000E114F10411F0D7011C92C80115C0422FDF +:10911000469546954695D601A40FB11D422F47703E +:109120008B0102C0000F111F4A95E2F7A8015C9164 +:10913000452B4C93622EA2CFA096EFE00C940E40EC +:10914000A0E0B0E0E6EAF8E40C94F23F6C01EB0139 +:109150005A01FC0117821682512CF601E380FE01B0 +:10916000E3FC8591E3FE8191182FEF01882309F438 +:10917000EEC090E00E94B149892B21F0C6010E9407 +:109180003747EBCF153241F4FE01E3FC1591E3FEC6 +:109190001191EF01153281F4C6010E94C54997FD76 +:1091A000D4C0412F50E09C01332724173507A9F282 +:1091B000B6010E94034ACBC01A3239F4E3FC159180 +:1091C000E3FE1191EF0101E001C000E0F12C20ED80 +:1091D000210F2A3080F402606F2D70E080E090E073 +:1091E00040E20E941047F62EFE01E3FC1591E3FEDB +:1091F0001191EF01ECCF01FF03C0F11003C0A7C034 +:10920000FF24FA94183619F01C3651F010C0FE01F4 +:10921000E3FC1591E3FE1191EF01183641F408606B +:109220000460FE01E3FC1591E3FE1191EF011123AF +:1092300009F48DC0612F70E080E092E00E94BA498D +:10924000892B09F484C000FD07C0F501808091805E +:10925000C50102965C0102C0812C912C1E3651F48E +:10926000F6014681578160E070E0202FC4010E9422 +:10927000044773CF1336A9F401FD02C0FF24F39411 +:10928000C6010E94C54997FD60C08114910429F070 +:10929000F4018083C40101964C01FA94F110F0CFDF +:1092A00050C01B3559F49E01A4016F2DC6010E94C8 +:1092B000E247EC01892B09F044C03EC0C6010E9480 +:1092C000374797FD42C01F3661F128F4143639F153 +:1092D000193651F128C0133771F0153701F123C049 +:1092E0008114910429F0F4016082C40101964C01BB +:1092F000FA94FF2071F0C6010E94C5493C0197FD18 +:1093000008C00E94B149892B59F3B601C3010E94DC +:10931000034A81149104A9F0F401108212C0006282 +:1093200003C0006101C00064202FA4016F2DC6019D +:109330000E945047811105C0F6018381807329F492 +:1093400006C000FD0ACF539408CF552019F0852D93 +:1093500090E002C08FEF9FEFCDB7DEB7EFE00C9447 +:109360000E4091110C941C4A803219F0895085509E +:10937000C8F70895FC010590061621F00020D9F7E2 +:10938000C00108953197CF010895CF93DF93EC0189 +:109390002B8120FF33C026FF0AC02F7B2B838E81B9 +:1093A0009F8101969F838E838A8190E029C022FF4E +:1093B0000FC0E881F9818081082E000C990B00977D +:1093C00019F420622B831AC03196F983E8830EC00A +:1093D000EA85FB85099597FF09C02B81019611F05D +:1093E00080E201C080E1822B8B8308C02E813F8107 +:1093F0002F5F3F4F3F832E83992702C08FEF9FEF50 +:10940000DF91CF910895FB01238120FF12C026FD3B +:1094100010C08F3F3FEF930761F082832F7D206460 +:109420002383268137812150310937832683992769 +:1094300008958FEF9FEF0895992788270895F8944E +:02944000FFCF5C +:10944200FFFF00000001000000000000C30900004F +:109452000000000200000000E40900000000030315 +:109462002C022E02300232023402360238023A0252 +:109472000000000200000000DE37000000003F0490 +:109482000403730295036C02610256025C045A04DF +:109492005804550453044F044D044804460442043E +:1094A2002B042604160533052305D108C608BC087B +:1094B20001040000000271CB257800424D4532388C +:1094C200300000000000F0203F22371A261A000068 +:1094D2000000AE229B22A5225F3E3F00000000005A +:1094E200772352236923412300000000CF269926C7 +:1094F200B726623E0000000053298D280329653EED +:1095020000000000262AD029D729683E000000006A +:109512007E2A692A6A2A6B3E00000000522C382CEF +:109522001F2C6E3E00000000562EC62DD62D713E19 +:1095320000000000642F582F5F2F743E00000000CF +:10954200B82FAC2FB32F773E00000000B630883022 +:109552009F307A3E0000000041332B313C314431D0 +:1095620000000000A936533675367D3E41004200A8 +:1095720043004400450046004700445000000000FC +:10958200007B374D375437803E000000001938EF1A +:06959200370838833E009B +:00000001FF diff --git a/software/deye-sun-12k/nano-644/src b/software/deye-sun-12k/nano-644/src new file mode 120000 index 0000000..5cd551c --- /dev/null +++ b/software/deye-sun-12k/nano-644/src @@ -0,0 +1 @@ +../src \ No newline at end of file diff --git a/software/deye-sun-12k/src/adafruit/bme280.cpp b/software/deye-sun-12k/src/adafruit/bme280.cpp new file mode 100644 index 0000000..1836c56 --- /dev/null +++ b/software/deye-sun-12k/src/adafruit/bme280.cpp @@ -0,0 +1,512 @@ +#include "bme280.h" +#include +#include + +Adafruit_BME280 theBME280; +Adafruit_BME280_Temp bm280TempSensor; +Adafruit_BME280_Pressure bm280PressureSensor; +Adafruit_BME280_Humidity bm280HumiditySensor; + +Adafruit_BME280::Adafruit_BME280() { + static I2cMaster i2cDevice; + t_fine_adjust = 0; + temp_sensor = &bm280TempSensor; + pressure_sensor = &bm280PressureSensor; + humidity_sensor = &bm280HumiditySensor; + i2c_dev = &i2cDevice; +} + +bool Adafruit_BME280::begin (uint8_t addr) { + if (!i2c_dev->begin(addr)) { + return false; + } + return init(); +} + +bool Adafruit_BME280::init() { + _sensorID = read8(BME280_REGISTER_CHIPID); + if (_sensorID != 0x60) { + return false; + } + write8(BME280_REGISTER_SOFTRESET, 0xB6); + _delay_ms(10); // wait for chip to wake up. + + // if chip is still reading calibration, delay + while (isReadingCalibration()) { + _delay_ms(10); + } + + readCoefficients(); // read trimming parameters, see DS 4.2.2 + setSampling(); // use defaults + _delay_ms(100); + + return true; +} + +/*! + * @brief setup sensor with given parameters / settings + * + * This is simply a overload to the normal begin()-function, so SPI users + * don't get confused about the library requiring an address. + * @param mode the power mode to use for the sensor + * @param tempSampling the temp samping rate to use + * @param pressSampling the pressure sampling rate to use + * @param humSampling the humidity sampling rate to use + * @param filter the filter mode to use + * @param duration the standby duration to use + */ +void Adafruit_BME280::setSampling(sensor_mode mode, + sensor_sampling tempSampling, + sensor_sampling pressSampling, + sensor_sampling humSampling, + sensor_filter filter, + standby_duration duration) { + _measReg.mode = mode; + _measReg.osrs_t = tempSampling; + _measReg.osrs_p = pressSampling; + + _humReg.osrs_h = humSampling; + _configReg.filter = filter; + _configReg.t_sb = duration; + _configReg.spi3w_en = 0; + + // making sure sensor is in sleep mode before setting configuration + // as it otherwise may be ignored + write8(BME280_REGISTER_CONTROL, MODE_SLEEP); + + // you must make sure to also set REGISTER_CONTROL after setting the + // CONTROLHUMID register, otherwise the values won't be applied (see + // DS 5.4.3) + write8(BME280_REGISTER_CONTROLHUMID, _humReg.get()); + write8(BME280_REGISTER_CONFIG, _configReg.get()); + write8(BME280_REGISTER_CONTROL, _measReg.get()); +} + +/*! + * @brief Writes an 8 bit value over I2C or SPI + * @param reg the register address to write to + * @param value the value to write to the register + */ +void Adafruit_BME280::write8(uint8_t reg, uint8_t value) { + uint8_t buffer[2]; + buffer[1] = value; + if (i2c_dev) { + buffer[0] = reg; + i2c_dev->write(buffer, 2); + } +} + +/*! + * @brief Reads an 8 bit value over I2C or SPI + * @param reg the register address to read from + * @returns the data byte read from the device + */ +uint8_t Adafruit_BME280::read8(uint8_t reg) { + uint8_t buffer[1]; + if (i2c_dev) { + buffer[0] = uint8_t(reg); + i2c_dev->write_then_read(buffer, 1, buffer, 1); + } + return buffer[0]; +} + +/*! + * @brief Reads a 16 bit value over I2C or SPI + * @param reg the register address to read from + * @returns the 16 bit data value read from the device + */ +uint16_t Adafruit_BME280::read16(uint8_t reg) { + uint8_t buffer[2]; + + if (i2c_dev) { + buffer[0] = uint8_t(reg); + i2c_dev->write_then_read(buffer, 1, buffer, 2); + } + return uint16_t(buffer[0]) << 8 | uint16_t(buffer[1]); +} + +/*! + * @brief Reads a signed 16 bit little endian value over I2C or SPI + * @param reg the register address to read from + * @returns the 16 bit data value read from the device + */ +uint16_t Adafruit_BME280::read16_LE(uint8_t reg) { + uint16_t temp = read16(reg); + return (temp >> 8) | (temp << 8); +} + +/*! + * @brief Reads a signed 16 bit value over I2C or SPI + * @param reg the register address to read from + * @returns the 16 bit data value read from the device + */ +int16_t Adafruit_BME280::readS16(uint8_t reg) { return (int16_t)read16(reg); } + +/*! + * @brief Reads a signed little endian 16 bit value over I2C or SPI + * @param reg the register address to read from + * @returns the 16 bit data value read from the device + */ +int16_t Adafruit_BME280::readS16_LE(uint8_t reg) { + return (int16_t)read16_LE(reg); +} + +/*! + * @brief Reads a 24 bit value over I2C + * @param reg the register address to read from + * @returns the 24 bit data value read from the device + */ +uint32_t Adafruit_BME280::read24(uint8_t reg) { + uint8_t buffer[3]; + + if (i2c_dev) { + buffer[0] = uint8_t(reg); + i2c_dev->write_then_read(buffer, 1, buffer, 3); + } + return uint32_t(buffer[0]) << 16 | uint32_t(buffer[1]) << 8 | + uint32_t(buffer[2]); +} + +/*! + * @brief Take a new measurement (only possible in forced mode) + @returns true in case of success else false + */ +bool Adafruit_BME280::takeForcedMeasurement(void) { + bool return_value = false; + // If we are in forced mode, the BME sensor goes back to sleep after each + // measurement and we need to set it to forced mode once at this point, so + // it will take the next measurement and then return to sleep again. + // In normal mode simply does new measurements periodically. + if (_measReg.mode == MODE_FORCED) { + return_value = true; + // set to forced mode, i.e. "take next measurement" + write8(BME280_REGISTER_CONTROL, _measReg.get()); + // Store current time to measure the timeout + uint32_t timeout_start = millis(); + // wait until measurement has been completed, otherwise we would read the + // the values from the last measurement or the timeout occurred after 2 sec. + while (read8(BME280_REGISTER_STATUS) & 0x08) { + // In case of a timeout, stop the while loop + if ((millis() - timeout_start) > 2000) { + return_value = false; + break; + } + _delay_ms(1); + } + } + return return_value; +} + +/*! + * @brief Reads the factory-set coefficients + */ +void Adafruit_BME280::readCoefficients(void) { + _bme280_calib.dig_T1 = read16_LE(BME280_REGISTER_DIG_T1); + _bme280_calib.dig_T2 = readS16_LE(BME280_REGISTER_DIG_T2); + _bme280_calib.dig_T3 = readS16_LE(BME280_REGISTER_DIG_T3); + + _bme280_calib.dig_P1 = read16_LE(BME280_REGISTER_DIG_P1); + _bme280_calib.dig_P2 = readS16_LE(BME280_REGISTER_DIG_P2); + _bme280_calib.dig_P3 = readS16_LE(BME280_REGISTER_DIG_P3); + _bme280_calib.dig_P4 = readS16_LE(BME280_REGISTER_DIG_P4); + _bme280_calib.dig_P5 = readS16_LE(BME280_REGISTER_DIG_P5); + _bme280_calib.dig_P6 = readS16_LE(BME280_REGISTER_DIG_P6); + _bme280_calib.dig_P7 = readS16_LE(BME280_REGISTER_DIG_P7); + _bme280_calib.dig_P8 = readS16_LE(BME280_REGISTER_DIG_P8); + _bme280_calib.dig_P9 = readS16_LE(BME280_REGISTER_DIG_P9); + + _bme280_calib.dig_H1 = read8(BME280_REGISTER_DIG_H1); + _bme280_calib.dig_H2 = readS16_LE(BME280_REGISTER_DIG_H2); + _bme280_calib.dig_H3 = read8(BME280_REGISTER_DIG_H3); + _bme280_calib.dig_H4 = ((int8_t)read8(BME280_REGISTER_DIG_H4) << 4) | + (read8(BME280_REGISTER_DIG_H4 + 1) & 0xF); + _bme280_calib.dig_H5 = ((int8_t)read8(BME280_REGISTER_DIG_H5 + 1) << 4) | + (read8(BME280_REGISTER_DIG_H5) >> 4); + _bme280_calib.dig_H6 = (int8_t)read8(BME280_REGISTER_DIG_H6); +} + +/*! + * @brief return true if chip is busy reading cal data + * @returns true if reading calibration, false otherwise + */ +bool Adafruit_BME280::isReadingCalibration(void) { + uint8_t const rStatus = read8(BME280_REGISTER_STATUS); + + return (rStatus & (1 << 0)) != 0; +} + +/*! + * @brief Returns the temperature from the sensor + * @returns the temperature read from the device + */ +float Adafruit_BME280::readTemperature(void) { + int32_t var1, var2; + + int32_t adc_T = read24(BME280_REGISTER_TEMPDATA); + if (adc_T == 0x800000) // value in case temp measurement was disabled + return NAN; + adc_T >>= 4; + + var1 = (int32_t)((adc_T / 8) - ((int32_t)_bme280_calib.dig_T1 * 2)); + var1 = (var1 * ((int32_t)_bme280_calib.dig_T2)) / 2048; + var2 = (int32_t)((adc_T / 16) - ((int32_t)_bme280_calib.dig_T1)); + var2 = (((var2 * var2) / 4096) * ((int32_t)_bme280_calib.dig_T3)) / 16384; + + t_fine = var1 + var2 + t_fine_adjust; + + int32_t T = (t_fine * 5 + 128) / 256; + + return (float)T / 100; +} + +/*! + * @brief Returns the pressure from the sensor + * @returns the pressure value (in Pascal) read from the device + */ +float Adafruit_BME280::readPressure(void) { + int64_t var1, var2, var3, var4; + + readTemperature(); // must be done first to get t_fine + + int32_t adc_P = read24(BME280_REGISTER_PRESSUREDATA); + if (adc_P == 0x800000) // value in case pressure measurement was disabled + return NAN; + adc_P >>= 4; + + var1 = ((int64_t)t_fine) - 128000; + var2 = var1 * var1 * (int64_t)_bme280_calib.dig_P6; + var2 = var2 + ((var1 * (int64_t)_bme280_calib.dig_P5) * 131072); + var2 = var2 + (((int64_t)_bme280_calib.dig_P4) * 34359738368); + var1 = ((var1 * var1 * (int64_t)_bme280_calib.dig_P3) / 256) + + ((var1 * ((int64_t)_bme280_calib.dig_P2) * 4096)); + var3 = ((int64_t)1) * 140737488355328; + var1 = (var3 + var1) * ((int64_t)_bme280_calib.dig_P1) / 8589934592; + + if (var1 == 0) { + return 0; // avoid exception caused by division by zero + } + + var4 = 1048576 - adc_P; + var4 = (((var4 * 2147483648UL) - var2) * 3125) / var1; + var1 = (((int64_t)_bme280_calib.dig_P9) * (var4 / 8192) * (var4 / 8192)) / + 33554432; + var2 = (((int64_t)_bme280_calib.dig_P8) * var4) / 524288; + var4 = ((var4 + var1 + var2) / 256) + (((int64_t)_bme280_calib.dig_P7) * 16); + + float P = var4 / 256.0; + + return P; +} + +/*! + * @brief Returns the humidity from the sensor + * @returns the humidity value read from the device + */ +float Adafruit_BME280::readHumidity(void) { + int32_t var1, var2, var3, var4, var5; + + readTemperature(); // must be done first to get t_fine + + int32_t adc_H = read16(BME280_REGISTER_HUMIDDATA); + if (adc_H == 0x8000) // value in case humidity measurement was disabled + return NAN; + + var1 = t_fine - ((int32_t)76800); + var2 = (int32_t)(adc_H * 16384); + var3 = (int32_t)(((int32_t)_bme280_calib.dig_H4) * 1048576); + var4 = ((int32_t)_bme280_calib.dig_H5) * var1; + var5 = (((var2 - var3) - var4) + (int32_t)16384) / 32768; + var2 = (var1 * ((int32_t)_bme280_calib.dig_H6)) / 1024; + var3 = (var1 * ((int32_t)_bme280_calib.dig_H3)) / 2048; + var4 = ((var2 * (var3 + (int32_t)32768)) / 1024) + (int32_t)2097152; + var2 = ((var4 * ((int32_t)_bme280_calib.dig_H2)) + 8192) / 16384; + var3 = var5 * var2; + var4 = ((var3 / 32768) * (var3 / 32768)) / 128; + var5 = var3 - ((var4 * ((int32_t)_bme280_calib.dig_H1)) / 16); + var5 = (var5 < 0 ? 0 : var5); + var5 = (var5 > 419430400 ? 419430400 : var5); + uint32_t H = (uint32_t)(var5 / 4096); + + return (float)H / 1024.0; +} + +/*! + * Calculates the altitude (in meters) from the specified atmospheric + * pressure (in hPa), and sea-level pressure (in hPa). + * @param seaLevel Sea-level pressure in hPa + * @returns the altitude value read from the device + */ +float Adafruit_BME280::readAltitude(float seaLevel) { + // Equation taken from BMP180 datasheet (page 16): + // http://www.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf + + // Note that using the equation from wikipedia can give bad results + // at high altitude. See this thread for more information: + // http://forums.adafruit.com/viewtopic.php?f=22&t=58064 + + float atmospheric = readPressure() / 100.0F; + return 44330.0 * (1.0 - pow(atmospheric / seaLevel, 0.1903)); +} + +/*! + * Calculates the pressure at sea level (in hPa) from the specified + * altitude (in meters), and atmospheric pressure (in hPa). + * @param altitude Altitude in meters + * @param atmospheric Atmospheric pressure in hPa + * @returns the pressure at sea level (in hPa) from the specified altitude + */ +float Adafruit_BME280::seaLevelForAltitude(float altitude, float atmospheric) { + // Equation taken from BMP180 datasheet (page 17): + // http://www.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf + + // Note that using the equation from wikipedia can give bad results + // at high altitude. See this thread for more information: + // http://forums.adafruit.com/viewtopic.php?f=22&t=58064 + + return atmospheric / pow(1.0 - (altitude / 44330.0), 5.255); +} + +/*! + * Returns Sensor ID found by init() for diagnostics + * @returns Sensor ID 0x60 for BME280, 0x56, 0x57, 0x58 BMP280 + */ +uint32_t Adafruit_BME280::sensorID(void) { return _sensorID; } + +/*! + * Returns the current temperature compensation value in degrees Celsius + * @returns the current temperature compensation value in degrees Celsius + */ +float Adafruit_BME280::getTemperatureCompensation(void) { + return float((t_fine_adjust * 5) >> 8) / 100.0; +}; + +/*! + * Sets a value to be added to each temperature reading. This adjusted + * temperature is used in pressure and humidity readings. + * @param adjustment Value to be added to each temperature reading in Celsius + */ +void Adafruit_BME280::setTemperatureCompensation(float adjustment) { + // convert the value in C into and adjustment to t_fine + t_fine_adjust = ((int32_t(adjustment * 100) << 8)) / 5; +}; + + +/**************************************************************************/ +/*! + @brief Gets the sensor_t data for the BME280's temperature sensor +*/ +/**************************************************************************/ +void Adafruit_BME280_Temp::getSensor(sensor_t *sensor) { + /* Clear the sensor_t object */ + memset(sensor, 0, sizeof(sensor_t)); + + /* Insert the sensor name in the fixed length char array */ + strncpy(sensor->name, "BME280", sizeof(sensor->name) - 1); + sensor->name[sizeof(sensor->name) - 1] = 0; + sensor->version = 1; + sensor->sensor_id = _sensorID; + sensor->type = SENSOR_TYPE_AMBIENT_TEMPERATURE; + sensor->min_delay = 0; + sensor->min_value = -40.0; /* Temperature range -40 ~ +85 C */ + sensor->max_value = +85.0; + sensor->resolution = 0.01; /* 0.01 C */ +} + +/**************************************************************************/ +/*! + @brief Gets the temperature as a standard sensor event + @param event Sensor event object that will be populated + @returns True +*/ +/**************************************************************************/ +bool Adafruit_BME280_Temp::getEvent(sensors_event_t *event) { + /* Clear the event */ + memset(event, 0, sizeof(sensors_event_t)); + + event->version = sizeof(sensors_event_t); + event->sensor_id = _sensorID; + event->type = SENSOR_TYPE_AMBIENT_TEMPERATURE; + event->timestamp = millis(); + event->temperature = theBME280.readTemperature(); + return true; +} + +/**************************************************************************/ +/*! + @brief Gets the sensor_t data for the BME280's pressure sensor +*/ +/**************************************************************************/ +void Adafruit_BME280_Pressure::getSensor(sensor_t *sensor) { + /* Clear the sensor_t object */ + memset(sensor, 0, sizeof(sensor_t)); + + /* Insert the sensor name in the fixed length char array */ + strncpy(sensor->name, "BME280", sizeof(sensor->name) - 1); + sensor->name[sizeof(sensor->name) - 1] = 0; + sensor->version = 1; + sensor->sensor_id = _sensorID; + sensor->type = SENSOR_TYPE_PRESSURE; + sensor->min_delay = 0; + sensor->min_value = 300.0; /* 300 ~ 1100 hPa */ + sensor->max_value = 1100.0; + sensor->resolution = 0.012; /* 0.12 hPa relative */ +} + +/**************************************************************************/ +/*! + @brief Gets the pressure as a standard sensor event + @param event Sensor event object that will be populated + @returns True +*/ +/**************************************************************************/ +bool Adafruit_BME280_Pressure::getEvent(sensors_event_t *event) { + /* Clear the event */ + memset(event, 0, sizeof(sensors_event_t)); + + event->version = sizeof(sensors_event_t); + event->sensor_id = _sensorID; + event->type = SENSOR_TYPE_PRESSURE; + event->timestamp = millis(); + event->pressure = theBME280.readPressure() / 100; // convert Pa to hPa + return true; +} + +/**************************************************************************/ +/*! + @brief Gets the sensor_t data for the BME280's humidity sensor +*/ +/**************************************************************************/ +void Adafruit_BME280_Humidity::getSensor(sensor_t *sensor) { + /* Clear the sensor_t object */ + memset(sensor, 0, sizeof(sensor_t)); + + /* Insert the sensor name in the fixed length char array */ + strncpy(sensor->name, "BME280", sizeof(sensor->name) - 1); + sensor->name[sizeof(sensor->name) - 1] = 0; + sensor->version = 1; + sensor->sensor_id = _sensorID; + sensor->type = SENSOR_TYPE_RELATIVE_HUMIDITY; + sensor->min_delay = 0; + sensor->min_value = 0; + sensor->max_value = 100; /* 0 - 100 % */ + sensor->resolution = 3; /* 3% accuracy */ +} + +/**************************************************************************/ +/*! + @brief Gets the humidity as a standard sensor event + @param event Sensor event object that will be populated + @returns True +*/ +/**************************************************************************/ +bool Adafruit_BME280_Humidity::getEvent(sensors_event_t *event) { + /* Clear the event */ + memset(event, 0, sizeof(sensors_event_t)); + + event->version = sizeof(sensors_event_t); + event->sensor_id = _sensorID; + event->type = SENSOR_TYPE_RELATIVE_HUMIDITY; + event->timestamp = millis(); + event->relative_humidity = theBME280.readHumidity(); + return true; +} diff --git a/software/deye-sun-12k/src/adafruit/bme280.h b/software/deye-sun-12k/src/adafruit/bme280.h new file mode 100644 index 0000000..aa5aa72 --- /dev/null +++ b/software/deye-sun-12k/src/adafruit/bme280.h @@ -0,0 +1,373 @@ +// https://github.com/adafruit/Adafruit_BME280_Library + +/*! + * @file Adafruit_BME280.h + * + * Designed specifically to work with the Adafruit BME280 Breakout + * ----> http://www.adafruit.com/products/2650 + * + * These sensors use I2C or SPI to communicate, 2 or 4 pins are required + * to interface. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Written by Kevin "KTOWN" Townsend for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * See the LICENSE file for details. + * + */ + +#ifndef __BME280_H__ +#define __BME280_H__ + +// #include "Arduino.h" + +// #include +// #include +// #include + + +#include "../i2cmaster.hpp" +#include "../main.hpp" +// #define byte uint8_t + + + +#include +#include +#include "sensor.h" + +/*! + * @brief default I2C address + */ +#define BME280_ADDRESS (0x77) // Primary I2C Address + /*! + * @brief alternate I2C address + */ +#define BME280_ADDRESS_ALTERNATE (0x76) // Alternate Address + +/*! + * @brief Register addresses + */ +enum { + BME280_REGISTER_DIG_T1 = 0x88, + BME280_REGISTER_DIG_T2 = 0x8A, + BME280_REGISTER_DIG_T3 = 0x8C, + + BME280_REGISTER_DIG_P1 = 0x8E, + BME280_REGISTER_DIG_P2 = 0x90, + BME280_REGISTER_DIG_P3 = 0x92, + BME280_REGISTER_DIG_P4 = 0x94, + BME280_REGISTER_DIG_P5 = 0x96, + BME280_REGISTER_DIG_P6 = 0x98, + BME280_REGISTER_DIG_P7 = 0x9A, + BME280_REGISTER_DIG_P8 = 0x9C, + BME280_REGISTER_DIG_P9 = 0x9E, + + BME280_REGISTER_DIG_H1 = 0xA1, + BME280_REGISTER_DIG_H2 = 0xE1, + BME280_REGISTER_DIG_H3 = 0xE3, + BME280_REGISTER_DIG_H4 = 0xE4, + BME280_REGISTER_DIG_H5 = 0xE5, + BME280_REGISTER_DIG_H6 = 0xE7, + + BME280_REGISTER_CHIPID = 0xD0, + BME280_REGISTER_VERSION = 0xD1, + BME280_REGISTER_SOFTRESET = 0xE0, + + BME280_REGISTER_CAL26 = 0xE1, // R calibration stored in 0xE1-0xF0 + + BME280_REGISTER_CONTROLHUMID = 0xF2, + BME280_REGISTER_STATUS = 0XF3, + BME280_REGISTER_CONTROL = 0xF4, + BME280_REGISTER_CONFIG = 0xF5, + BME280_REGISTER_PRESSUREDATA = 0xF7, + BME280_REGISTER_TEMPDATA = 0xFA, + BME280_REGISTER_HUMIDDATA = 0xFD +}; + +/**************************************************************************/ +/*! + @brief calibration data +*/ +/**************************************************************************/ +typedef struct { + uint16_t dig_T1; ///< temperature compensation value + int16_t dig_T2; ///< temperature compensation value + int16_t dig_T3; ///< temperature compensation value + + uint16_t dig_P1; ///< pressure compensation value + int16_t dig_P2; ///< pressure compensation value + int16_t dig_P3; ///< pressure compensation value + int16_t dig_P4; ///< pressure compensation value + int16_t dig_P5; ///< pressure compensation value + int16_t dig_P6; ///< pressure compensation value + int16_t dig_P7; ///< pressure compensation value + int16_t dig_P8; ///< pressure compensation value + int16_t dig_P9; ///< pressure compensation value + + uint8_t dig_H1; ///< humidity compensation value + int16_t dig_H2; ///< humidity compensation value + uint8_t dig_H3; ///< humidity compensation value + int16_t dig_H4; ///< humidity compensation value + int16_t dig_H5; ///< humidity compensation value + int8_t dig_H6; ///< humidity compensation value +} bme280_calib_data; +/*=========================================================================*/ + +class Adafruit_BME280; + +/** Adafruit Unified Sensor interface for temperature component of BME280 */ +class Adafruit_BME280_Temp : public Adafruit_Sensor { +public: + /** @brief Create an Adafruit_Sensor compatible object for the temp sensor + @param parent A pointer to the BME280 class */ + Adafruit_BME280_Temp() { _sensorID = 280; } + bool getEvent(sensors_event_t *); + void getSensor(sensor_t *); + +private: + int _sensorID; +}; + +/** Adafruit Unified Sensor interface for pressure component of BME280 */ +class Adafruit_BME280_Pressure : public Adafruit_Sensor { +public: + /** @brief Create an Adafruit_Sensor compatible object for the pressure sensor + @param parent A pointer to the BME280 class */ + Adafruit_BME280_Pressure() { _sensorID = 280; } + bool getEvent(sensors_event_t *); + void getSensor(sensor_t *); + +private: + int _sensorID; +}; + +/** Adafruit Unified Sensor interface for humidity component of BME280 */ +class Adafruit_BME280_Humidity : public Adafruit_Sensor { +public: + /** @brief Create an Adafruit_Sensor compatible object for the humidity sensor + @param parent A pointer to the BME280 class */ + Adafruit_BME280_Humidity() { _sensorID = 280;} + bool getEvent(sensors_event_t *); + void getSensor(sensor_t *); + +private: + int _sensorID; +}; + +/**************************************************************************/ +/*! + @brief Class that stores state and functions for interacting with BME280 IC +*/ +/**************************************************************************/ +class Adafruit_BME280 { +public: + /**************************************************************************/ + /*! + @brief sampling rates + */ + /**************************************************************************/ + enum sensor_sampling { + SAMPLING_NONE = 0b000, + SAMPLING_X1 = 0b001, + SAMPLING_X2 = 0b010, + SAMPLING_X4 = 0b011, + SAMPLING_X8 = 0b100, + SAMPLING_X16 = 0b101 + }; + + /**************************************************************************/ + /*! + @brief power modes + */ + /**************************************************************************/ + enum sensor_mode { + MODE_SLEEP = 0b00, + MODE_FORCED = 0b01, + MODE_NORMAL = 0b11 + }; + + /**************************************************************************/ + /*! + @brief filter values + */ + /**************************************************************************/ + enum sensor_filter { + FILTER_OFF = 0b000, + FILTER_X2 = 0b001, + FILTER_X4 = 0b010, + FILTER_X8 = 0b011, + FILTER_X16 = 0b100 + }; + + /**************************************************************************/ + /*! + @brief standby duration in ms + */ + /**************************************************************************/ + enum standby_duration { + STANDBY_MS_0_5 = 0b000, + STANDBY_MS_10 = 0b110, + STANDBY_MS_20 = 0b111, + STANDBY_MS_62_5 = 0b001, + STANDBY_MS_125 = 0b010, + STANDBY_MS_250 = 0b011, + STANDBY_MS_500 = 0b100, + STANDBY_MS_1000 = 0b101 + }; + + // constructors + Adafruit_BME280(); + + bool begin(uint8_t addr = BME280_ADDRESS); + bool init(); + + void setSampling(sensor_mode mode = MODE_NORMAL, + sensor_sampling tempSampling = SAMPLING_X16, + sensor_sampling pressSampling = SAMPLING_X16, + sensor_sampling humSampling = SAMPLING_X16, + sensor_filter filter = FILTER_OFF, + standby_duration duration = STANDBY_MS_0_5); + + bool takeForcedMeasurement(void); + float readTemperature(void); + float readPressure(void); + float readHumidity(void); + + float readAltitude(float seaLevel); + float seaLevelForAltitude(float altitude, float pressure); + uint32_t sensorID(void); + + float getTemperatureCompensation(void); + void setTemperatureCompensation(float); + +protected: + I2cMaster *i2c_dev = NULL; ///< Pointer to I2C bus interface + // Adafruit_SPIDevice *spi_dev = NULL; ///< Pointer to SPI bus interface + + Adafruit_BME280_Temp *temp_sensor; + Adafruit_BME280_Pressure *pressure_sensor; + Adafruit_BME280_Humidity *humidity_sensor; + + void readCoefficients(void); + bool isReadingCalibration(void); + + void write8(uint8_t reg, uint8_t value); + uint8_t read8(uint8_t reg); + uint16_t read16(uint8_t reg); + uint32_t read24(uint8_t reg); + int16_t readS16(uint8_t reg); + uint16_t read16_LE(uint8_t reg); // little endian + int16_t readS16_LE(uint8_t reg); // little endian + + uint8_t _i2caddr; //!< I2C addr for the TwoWire interface + int32_t _sensorID; //!< ID of the BME Sensor + int32_t t_fine; //!< temperature with high resolution, stored as an attribute + //!< as this is used for temperature compensation reading + //!< humidity and pressure + + int32_t t_fine_adjust; //!< add to compensate temp readings and in turn + //!< to pressure and humidity readings + + bme280_calib_data _bme280_calib; //!< here calibration data is stored + + /**************************************************************************/ + /*! + @brief config register + */ + /**************************************************************************/ + struct config { + // inactive duration (standby time) in normal mode + // 000 = 0.5 ms + // 001 = 62.5 ms + // 010 = 125 ms + // 011 = 250 ms + // 100 = 500 ms + // 101 = 1000 ms + // 110 = 10 ms + // 111 = 20 ms + unsigned int t_sb : 3; ///< inactive duration (standby time) in normal mode + + // filter settings + // 000 = filter off + // 001 = 2x filter + // 010 = 4x filter + // 011 = 8x filter + // 100 and above = 16x filter + unsigned int filter : 3; ///< filter settings + + // unused - don't set + unsigned int none : 1; ///< unused - don't set + unsigned int spi3w_en : 1; ///< unused - don't set + + /// @return combined config register + unsigned int get() { return (t_sb << 5) | (filter << 2) | spi3w_en; } + }; + config _configReg; //!< config register object + + /**************************************************************************/ + /*! + @brief ctrl_meas register + */ + /**************************************************************************/ + struct ctrl_meas { + // temperature oversampling + // 000 = skipped + // 001 = x1 + // 010 = x2 + // 011 = x4 + // 100 = x8 + // 101 and above = x16 + unsigned int osrs_t : 3; ///< temperature oversampling + + // pressure oversampling + // 000 = skipped + // 001 = x1 + // 010 = x2 + // 011 = x4 + // 100 = x8 + // 101 and above = x16 + unsigned int osrs_p : 3; ///< pressure oversampling + + // device mode + // 00 = sleep + // 01 or 10 = forced + // 11 = normal + unsigned int mode : 2; ///< device mode + + /// @return combined ctrl register + unsigned int get() { return (osrs_t << 5) | (osrs_p << 2) | mode; } + }; + ctrl_meas _measReg; //!< measurement register object + + /**************************************************************************/ + /*! + @brief ctrl_hum register + */ + /**************************************************************************/ + struct ctrl_hum { + /// unused - don't set + unsigned int none : 5; + + // pressure oversampling + // 000 = skipped + // 001 = x1 + // 010 = x2 + // 011 = x4 + // 100 = x8 + // 101 and above = x16 + unsigned int osrs_h : 3; ///< pressure oversampling + + /// @return combined ctrl hum register + unsigned int get() { return (osrs_h); } + }; + ctrl_hum _humReg; //!< hum register object +}; + +extern Adafruit_BME280 theBME280; + +#endif diff --git a/software/deye-sun-12k/src/adafruit/ens160.cpp b/software/deye-sun-12k/src/adafruit/ens160.cpp new file mode 100644 index 0000000..29e3704 --- /dev/null +++ b/software/deye-sun-12k/src/adafruit/ens160.cpp @@ -0,0 +1,374 @@ +/* + ScioSense_ENS160.h - Library for the ENS160 sensor with I2C interface from ScioSense + 2023 Mar 23 v6 Christoph Friese Bugfix measurement routine, prepare next release + 2021 Nov 25 v5 Martin Herold Custom mode timing fixed + 2021 Feb 04 v4 Giuseppe de Pinto Custom mode fixed + 2020 Apr 06 v3 Christoph Friese Changed nomenclature to ScioSense as product shifted from ams + 2020 Feb 15 v2 Giuseppe Pasetti Corrected firmware flash option + 2019 May 05 v1 Christoph Friese Created + based on application note "ENS160 Software Integration.pdf" rev 0.01 +*/ + +#include "ens160.h" +#include "math.h" +#include +#include +#include + +ScioSense_ENS160::ScioSense_ENS160 () { + _revENS16x = 0; + + //Isotherm, HP0 252°C / HP1 350°C / HP2 250°C / HP3 324°C / measure every 1008ms + _seq_steps[0][0] = 0x7c; + _seq_steps[0][1] = 0x0a; + _seq_steps[0][2] = 0x7e; + _seq_steps[0][3] = 0xaf; + _seq_steps[0][4] = 0xaf; + _seq_steps[0][5] = 0xa2; + _seq_steps[0][6] = 0x00; + _seq_steps[0][7] = 0x80; +} + +bool ScioSense_ENS160::begin () { + i2cDevice.begin(ENS160_I2CADDR_1); + _delay_ms(ENS160_BOOTING); + if (reset()) { + if (checkPartID()) { + if (setMode(ENS160_OPMODE_IDLE)) { + if (clearCommand()) { + if (getFirmware()) { + return true; + } + } + } + } + } + return false; + +} + +bool ScioSense_ENS160::write8 (uint8_t reg, uint8_t value) { + uint8_t buffer[2]; + buffer[1] = value; + buffer[0] = reg; + return i2cDevice.write(buffer, 2); +} + +bool ScioSense_ENS160::read8 (uint8_t reg, uint8_t *value) { + uint8_t buffer[1]; + buffer[0] = uint8_t(reg); + return i2cDevice.write_then_read(buffer, 1, value, 1); +} + +bool ScioSense_ENS160::read16 (uint8_t reg, uint16_t *value) { + uint8_t buffer[1]; + buffer[0] = uint8_t(reg); + return i2cDevice.write_then_read(buffer, 1, (uint8_t *)value, 2); +} + +bool ScioSense_ENS160::read16LE (uint8_t reg, uint16_t *value) { + uint16_t tmp; + if (read16(reg, &tmp)) { + *value = ((tmp & 0xff) << 8) | (tmp >> 8); + return true; + } + return false; +} + +bool ScioSense_ENS160::readBytes (uint8_t reg, uint8_t *bytes, uint8_t len) { + uint8_t buffer[1]; + buffer[0] = uint8_t(reg); + return i2cDevice.write_then_read(buffer, 1, buffer, len); +} + + +// Sends a reset to the ENS160. Returns false on I2C problems. +bool ScioSense_ENS160::reset () { + if (write8(ENS160_REG_OPMODE, ENS160_OPMODE_RESET)) { + _delay_ms(ENS160_BOOTING); + return true; + } + _delay_ms(ENS160_BOOTING); + return false; +} + +// Reads the part ID and confirms valid sensor +bool ScioSense_ENS160::checkPartID () { + uint16_t part_id; + + read16(ENS160_REG_PART_ID, &part_id); + _delay_ms(ENS160_BOOTING); + + if (part_id == ENS160_PARTID) { + _revENS16x = 0; + return true; + + } else if (part_id == ENS161_PARTID) { + _revENS16x = 1; + return true; + } + + return false; +} + +// Initialize idle mode and confirms +bool ScioSense_ENS160::clearCommand () { + uint8_t status; + + if (write8(ENS160_REG_COMMAND, ENS160_COMMAND_NOP)) { + if (write8(ENS160_REG_COMMAND, ENS160_COMMAND_CLRGPR)) { + _delay_ms(ENS160_BOOTING); + if (read8(ENS160_REG_DATA_STATUS, &status)) { + return true; + } + } + } + _delay_ms(ENS160_BOOTING); + return false; +} + +// Read firmware revisions +bool ScioSense_ENS160::getFirmware () { + uint8_t i2cbuf[3]; + + if (clearCommand()) { + _delay_ms(ENS160_BOOTING); + if (write8(ENS160_REG_COMMAND, ENS160_COMMAND_GET_APPVER)) { + if (readBytes(ENS160_REG_GPR_READ_4, i2cbuf, 3)) { + _fw_ver_major = i2cbuf[0]; + _fw_ver_minor = i2cbuf[1]; + _fw_ver_build = i2cbuf[2]; + _revENS16x = this->_fw_ver_major > 6 ? 1 : 0; + _delay_ms(ENS160_BOOTING); + return true; + } + } + } + _delay_ms(ENS160_BOOTING); + return false; +} + +// Set operation mode of sensor +bool ScioSense_ENS160::setMode (uint8_t mode) { + //LP only valid for rev>0 + if ((mode == ENS160_OPMODE_LP) and (_revENS16x == 0)) { + return false; + } + if (write8(ENS160_REG_OPMODE, mode)) { + _delay_ms(ENS160_BOOTING); + return true; + } + _delay_ms(ENS160_BOOTING); + return false; +} + +// Initialize definition of custom mode with steps +bool ScioSense_ENS160::initCustomMode (uint16_t stepNum) { + if (stepNum > 0) { + _stepCount = stepNum; + if (setMode(ENS160_OPMODE_IDLE)) { + if (clearCommand()) { + if (write8(ENS160_REG_COMMAND, ENS160_COMMAND_SETSEQ)) { + _delay_ms(ENS160_BOOTING); + return true; + } + } + } + } + _delay_ms(ENS160_BOOTING); + return false; +} + +// Add a step to custom measurement profile with definition of duration, enabled data acquisition and temperature for each hotplate +bool ScioSense_ENS160::addCustomStep (uint16_t time, bool measureHP0, bool measureHP1, bool measureHP2, bool measureHP3, uint16_t tempHP0, uint16_t tempHP1, uint16_t tempHP2, uint16_t tempHP3) { + uint8_t seq_ack; + uint8_t temp; + + _delay_ms(ENS160_BOOTING); + + temp = (uint8_t)(((time / 24) - 1) << 6); + if (measureHP0) { + temp = temp | 0x20; + } + if (measureHP1) { + temp = temp | 0x10; + } + if (measureHP2) { + temp = temp | 0x08; + } + if (measureHP3) { + temp = temp | 0x04; + } + if (!write8(ENS160_REG_GPR_WRITE_0, temp)) { + return false; + } + temp = (uint8_t)(((time / 24) - 1) >> 2); + if (!write8(ENS160_REG_GPR_WRITE_1, temp)) { + return false; + } + if (!write8(ENS160_REG_GPR_WRITE_2, (uint8_t)(tempHP0 / 2))) { + return false; + } + if (!write8(ENS160_REG_GPR_WRITE_3, (uint8_t)(tempHP1 / 2))) { + return false; + } + if (write8(ENS160_REG_GPR_WRITE_4, (uint8_t)(tempHP2 / 2))) { + return false; + } + if (write8(ENS160_REG_GPR_WRITE_5, (uint8_t)(tempHP3 / 2))) { + return false; + } + + if (write8(ENS160_REG_GPR_WRITE_6, (uint8_t)(_stepCount - 1))) { + return false; + } + + if (_stepCount == 1) { + if (!write8(ENS160_REG_GPR_WRITE_7, 128)) { + return false; + } + } else { + if (!write8(ENS160_REG_GPR_WRITE_7, 0)) { + return false; + } + } + _delay_ms(ENS160_BOOTING); + + if (!read8(ENS160_REG_GPR_READ_7, &seq_ack)) { + return false; + } + _delay_ms(ENS160_BOOTING); + + if ((ENS160_SEQ_ACK_COMPLETE | _stepCount) != seq_ack) { + _stepCount++; + return false; + } + + return true; +} + +bool ScioSense_ENS160::readStatus (uint8_t *status) { + return read8(ENS160_REG_DATA_STATUS, status); +} + +bool ScioSense_ENS160::readData (ENS160_DATA *data) { + uint8_t buffer[1] = { 0x21 }; + return i2cDevice.write_then_read(buffer, 1, (uint8_t *)data, sizeof(ENS160_DATA)); +} + +// Perform prediction measurement and stores result in internal variables +bool ScioSense_ENS160::measure (bool waitForNew) { + uint8_t i2cbuf[8]; + uint8_t status; + + // Set default status for early bail out + if (waitForNew) { + do { + if (!read8(ENS160_REG_DATA_STATUS, &status)) { + return false; + } + _delay_ms(1); + } while (!IS_NEWDAT(status)); + } else { + if (!read8(ENS160_REG_DATA_STATUS, &status)) { + return false; + } + } + + + // Read predictions + if (IS_NEWDAT(status)) { + if (!readBytes(ENS160_REG_DATA_AQI, i2cbuf, 7)) { + return false; + } + return false; + _data_aqi = i2cbuf[0]; + _data_tvoc = i2cbuf[1] | ((uint16_t)i2cbuf[2] << 8); + _data_eco2 = i2cbuf[3] | ((uint16_t)i2cbuf[4] << 8); + if (_revENS16x > 0) { + _data_aqi500 = ((uint16_t)i2cbuf[5]) | ((uint16_t)i2cbuf[6] << 8); + } else { + _data_aqi500 = 0; + } + return true; + } + + return false; +} + +// Perfrom raw measurement and stores result in internal variables +bool ScioSense_ENS160::measureRaw (bool waitForNew) { + uint8_t i2cbuf[8]; + uint8_t status; + + // Set default status for early bail out + if (waitForNew) { + do { + _delay_ms(1); + if (!read8(ENS160_REG_DATA_STATUS, &status)) { + return false; + } + } while (!IS_NEWGPR(status)); + } else { + if (!read8(ENS160_REG_DATA_STATUS, &status)) { + return false; + } + } + + if (IS_NEWGPR(status)) { + + // Read raw resistance values + if (!readBytes(ENS160_REG_GPR_READ_0, i2cbuf, 8)) { + return false; + } + _hp0_rs = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[0] | ((uint16_t)i2cbuf[1] << 8))); + _hp1_rs = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[2] | ((uint16_t)i2cbuf[3] << 8))); + _hp2_rs = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[4] | ((uint16_t)i2cbuf[5] << 8))); + _hp3_rs = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[6] | ((uint16_t)i2cbuf[7] << 8))); + + // Read baselines + if (!readBytes(ENS160_REG_DATA_BL, i2cbuf, 8)) { + return false; + } + _hp0_bl = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[0] | ((uint16_t)i2cbuf[1] << 8))); + _hp1_bl = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[2] | ((uint16_t)i2cbuf[3] << 8))); + _hp2_bl = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[4] | ((uint16_t)i2cbuf[5] << 8))); + _hp3_bl = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[6] | ((uint16_t)i2cbuf[7] << 8))); + + if (!read8(ENS160_REG_DATA_MISR, i2cbuf)) { + return false; + } + _misr = i2cbuf[0]; + return true; + } + + return false; +} + + +// Writes t (degC) and h (%rh) to ENV_DATA. Returns false on I2C problems. +bool ScioSense_ENS160::set_envdata (float t, float h) { + uint16_t t_data = (uint16_t)((t + 273.15f) * 64.0f); + uint16_t rh_data = (uint16_t)(h * 512.0f); + return this->set_envdata210(t_data, rh_data); +} + +// Writes t and h (in ENS210 format) to ENV_DATA. Returns false on I2C problems. +bool ScioSense_ENS160::set_envdata210 (uint16_t t, uint16_t h) { + //uint16_t temp; + uint8_t trh_in[4]; + + //temp = (uint16_t)((t + 273.15f) * 64.0f); + trh_in[0] = t & 0xff; + trh_in[1] = (t >> 8) & 0xff; + + //temp = (uint16_t)(h * 512.0f); + trh_in[2] = h & 0xff; + trh_in[3] = (h >> 8) & 0xff; + + if (!i2cDevice.writeByteAndBuffer(ENS160_REG_TEMP_IN, trh_in, 4)) { + return false; + } + + return true; +} diff --git a/software/deye-sun-12k/src/adafruit/ens160.h b/software/deye-sun-12k/src/adafruit/ens160.h new file mode 100644 index 0000000..7f26ba1 --- /dev/null +++ b/software/deye-sun-12k/src/adafruit/ens160.h @@ -0,0 +1,188 @@ +/* + ScioSense_ENS160.h - Library for the ENS160 sensor with I2C interface from ScioSense + 2023 Mar 23 v6 Christoph Friese Bugfix measurement routine, prepare next release + 2021 July 29 v4 Christoph Friese Changed nomenclature to ScioSense as product shifted from ams + 2020 Apr 06 v3 Christoph Friese Changed nomenclature to ScioSense as product shifted from ams + 2020 Feb 15 v2 Giuseppe Pasetti Corrected firmware flash option + 2019 May 05 v1 Christoph Friese Created + based on application note "ENS160 Software Integration.pdf" rev 0.01 +*/ + +#ifndef __SCIOSENSE_ENS160_H_ +#define __SCIOSENSE_ENS160_H_ + +#include "../i2cmaster.hpp" +#include +// #define byte uint8_t + +// #if (ARDUINO >= 100) +// #include "Arduino.h" +// #else +// #include "WProgram.h" +// #endif + +// #include + +// Chip constants +#define ENS160_PARTID 0x0160 +#define ENS161_PARTID 0x0161 +#define ENS160_BOOTING 10 + +// 7-bit I2C slave address of the ENS160 +#define ENS160_I2CADDR_0 0x52 //ADDR low +#define ENS160_I2CADDR_1 0x53 //ADDR high + +// ENS160 registers for version V0 +#define ENS160_REG_PART_ID 0x00 // 2 byte register +#define ENS160_REG_OPMODE 0x10 +#define ENS160_REG_CONFIG 0x11 +#define ENS160_REG_COMMAND 0x12 +#define ENS160_REG_TEMP_IN 0x13 +#define ENS160_REG_RH_IN 0x15 +#define ENS160_REG_DATA_STATUS 0x20 +#define ENS160_REG_DATA_AQI 0x21 +#define ENS160_REG_DATA_TVOC 0x22 +#define ENS160_REG_DATA_ECO2 0x24 +#define ENS160_REG_DATA_BL 0x28 +#define ENS160_REG_DATA_T 0x30 +#define ENS160_REG_DATA_RH 0x32 +#define ENS160_REG_DATA_MISR 0x38 +#define ENS160_REG_GPR_WRITE_0 0x40 +#define ENS160_REG_GPR_WRITE_1 ENS160_REG_GPR_WRITE_0 + 1 +#define ENS160_REG_GPR_WRITE_2 ENS160_REG_GPR_WRITE_0 + 2 +#define ENS160_REG_GPR_WRITE_3 ENS160_REG_GPR_WRITE_0 + 3 +#define ENS160_REG_GPR_WRITE_4 ENS160_REG_GPR_WRITE_0 + 4 +#define ENS160_REG_GPR_WRITE_5 ENS160_REG_GPR_WRITE_0 + 5 +#define ENS160_REG_GPR_WRITE_6 ENS160_REG_GPR_WRITE_0 + 6 +#define ENS160_REG_GPR_WRITE_7 ENS160_REG_GPR_WRITE_0 + 7 +#define ENS160_REG_GPR_READ_0 0x48 +#define ENS160_REG_GPR_READ_4 ENS160_REG_GPR_READ_0 + 4 +#define ENS160_REG_GPR_READ_6 ENS160_REG_GPR_READ_0 + 6 +#define ENS160_REG_GPR_READ_7 ENS160_REG_GPR_READ_0 + 7 + +//ENS160 data register fields +#define ENS160_COMMAND_NOP 0x00 +#define ENS160_COMMAND_CLRGPR 0xCC +#define ENS160_COMMAND_GET_APPVER 0x0E +#define ENS160_COMMAND_SETTH 0x02 +#define ENS160_COMMAND_SETSEQ 0xC2 + +#define ENS160_OPMODE_RESET 0xF0 +#define ENS160_OPMODE_DEP_SLEEP 0x00 +#define ENS160_OPMODE_IDLE 0x01 +#define ENS160_OPMODE_STD 0x02 +#define ENS160_OPMODE_LP 0x03 +#define ENS160_OPMODE_CUSTOM 0xC0 + +#define ENS160_BL_CMD_START 0x02 +#define ENS160_BL_CMD_ERASE_APP 0x04 +#define ENS160_BL_CMD_ERASE_BLINE 0x06 +#define ENS160_BL_CMD_WRITE 0x08 +#define ENS160_BL_CMD_VERIFY 0x0A +#define ENS160_BL_CMD_GET_BLVER 0x0C +#define ENS160_BL_CMD_GET_APPVER 0x0E +#define ENS160_BL_CMD_EXITBL 0x12 + +#define ENS160_SEQ_ACK_NOTCOMPLETE 0x80 +#define ENS160_SEQ_ACK_COMPLETE 0xC0 + +#define IS_ENS160_SEQ_ACK_NOT_COMPLETE(x) (ENS160_SEQ_ACK_NOTCOMPLETE == (ENS160_SEQ_ACK_NOTCOMPLETE & (x))) +#define IS_ENS160_SEQ_ACK_COMPLETE(x) (ENS160_SEQ_ACK_COMPLETE == (ENS160_SEQ_ACK_COMPLETE & (x))) + +#define ENS160_DATA_STATUS_NEWDAT 0x02 +#define ENS160_DATA_STATUS_NEWGPR 0x01 + +#define IS_NEWDAT(x) (ENS160_DATA_STATUS_NEWDAT == (ENS160_DATA_STATUS_NEWDAT & (x))) +#define IS_NEWGPR(x) (ENS160_DATA_STATUS_NEWGPR == (ENS160_DATA_STATUS_NEWGPR & (x))) +#define IS_NEW_DATA_AVAILABLE(x) (0 != ((ENS160_DATA_STATUS_NEWDAT | ENS160_DATA_STATUS_NEWGPR ) & (x))) + +#define CONVERT_RS_RAW2OHMS_I(x) (1 << ((x) >> 11)) +#define CONVERT_RS_RAW2OHMS_F(x) (pow (2, (float)(x) / 2048)) + +typedef struct { + uint8_t aqi; + uint16_t tvoc; + uint16_t eco2; +} ENS160_DATA; + +class ScioSense_ENS160 { + + public: + ScioSense_ENS160(); + + void setI2C(uint8_t sda, uint8_t scl); // Function to redefine I2C pins + + bool begin(); // Init I2C communication, resets ENS160 and checks its PART_ID. Returns false on I2C problems or wrong PART_ID. + uint8_t revENS16x() { return this->_revENS16x; } // Report version of sensor (0: ENS160, 1: ENS161) + bool setMode(uint8_t mode); // Set operation mode of sensor + + bool initCustomMode(uint16_t stepNum); // Initialize definition of custom mode with steps + bool addCustomStep(uint16_t time, bool measureHP0, bool measureHP1, bool measureHP2, bool measureHP3, uint16_t tempHP0, uint16_t tempHP1, uint16_t tempHP2, uint16_t tempHP3); + // Add a step to custom measurement profile with definition of duration, enabled data acquisition and temperature for each hotplate + + bool readData (ENS160_DATA *data); + bool readStatus(uint8_t *status); + bool measure(bool waitForNew); // Perform measurement and stores result in internal variables + bool measureRaw(bool waitForNew); // Perform raw measurement and stores result in internal variables + bool set_envdata(float t, float h); // Writes t (degC) and h (%rh) to ENV_DATA. Returns "0" if I2C transmission is successful + bool set_envdata210(uint16_t t, uint16_t h); // Writes t and h (in ENS210 format) to ENV_DATA. Returns "0" if I2C transmission is successful + uint8_t getMajorRev() { return this->_fw_ver_major; } // Get major revision number of used firmware + uint8_t getMinorRev() { return this->_fw_ver_minor; } // Get minor revision number of used firmware + uint8_t getBuild() { return this->_fw_ver_build; } // Get build revision number of used firmware + + uint8_t getAQI() { return this->_data_aqi; } // Get AQI value of last measurement + uint16_t getTVOC() { return this->_data_tvoc; } // Get TVOC value of last measurement + uint16_t geteCO2() { return this->_data_eco2; } // Get eCO2 value of last measurement + uint16_t getAQI500() { return this->_data_aqi500; } // Get AQI500 value of last measurement + uint32_t getHP0() { return this->_hp0_rs; } // Get resistance of HP0 of last measurement + uint32_t getHP1() { return this->_hp1_rs; } // Get resistance of HP1 of last measurement + uint32_t getHP2() { return this->_hp2_rs; } // Get resistance of HP2 of last measurement + uint32_t getHP3() { return this->_hp3_rs; } // Get resistance of HP3 of last measurement + uint32_t getHP0BL() { return this->_hp0_bl; } // Get baseline resistance of HP0 of last measurement + uint32_t getHP1BL() { return this->_hp1_bl; } // Get baseline resistance of HP1 of last measurement + uint32_t getHP2BL() { return this->_hp2_bl; } // Get baseline resistance of HP2 of last measurement + uint32_t getHP3BL() { return this->_hp3_bl; } // Get baseline resistance of HP3 of last measurement + uint8_t getMISR() { return this->_misr; } // Return status code of sensor + + private: + I2cMaster i2cDevice; + bool reset(); // Sends a reset to the ENS160. Returns false on I2C problems. + bool checkPartID(); // Reads the part ID and confirms valid sensor + bool clearCommand(); // Initialize idle mode and confirms + bool getFirmware(); // Read firmware revisions + + uint8_t _revENS16x; // ENS160 or ENS161 connected? (FW >7) + + uint8_t _fw_ver_major; + uint8_t _fw_ver_minor; + uint8_t _fw_ver_build; + + uint16_t _stepCount; // Counter for custom sequence + + uint8_t _data_aqi; + uint16_t _data_tvoc; + uint16_t _data_eco2; + uint16_t _data_aqi500; + uint32_t _hp0_rs; + uint32_t _hp0_bl; + uint32_t _hp1_rs; + uint32_t _hp1_bl; + uint32_t _hp2_rs; + uint32_t _hp2_bl; + uint32_t _hp3_rs; + uint32_t _hp3_bl; + uint16_t _temp; + int _slaveaddr; // Slave address of the ENS160 + uint8_t _misr; + + uint8_t _seq_steps[1][8]; + + bool write8(uint8_t reg, uint8_t value); + bool read8 (uint8_t reg, uint8_t *value); + bool read16 (uint8_t reg, uint16_t *value); + bool read16LE (uint8_t reg, uint16_t *value); + bool readBytes (uint8_t reg, uint8_t *bytes, uint8_t len); +}; + + +#endif diff --git a/software/deye-sun-12k/src/adafruit/sensor.h b/software/deye-sun-12k/src/adafruit/sensor.h new file mode 100644 index 0000000..ac7e454 --- /dev/null +++ b/software/deye-sun-12k/src/adafruit/sensor.h @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software< /span> + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Update by K. Townsend (Adafruit Industries) for lighter typedefs, and + * extended sensor support to include color, voltage and current */ + +#ifndef _ADAFRUIT_SENSOR_H +#define _ADAFRUIT_SENSOR_H + +#ifndef ARDUINO +#include +#elif ARDUINO >= 100 +#include "Arduino.h" +#include "Print.h" +#else +#include "WProgram.h" +#endif + +/* Constants */ +#define SENSORS_GRAVITY_EARTH (9.80665F) /**< Earth's gravity in m/s^2 */ +#define SENSORS_GRAVITY_MOON (1.6F) /**< The moon's gravity in m/s^2 */ +#define SENSORS_GRAVITY_SUN (275.0F) /**< The sun's gravity in m/s^2 */ +#define SENSORS_GRAVITY_STANDARD (SENSORS_GRAVITY_EARTH) +#define SENSORS_MAGFIELD_EARTH_MAX \ + (60.0F) /**< Maximum magnetic field on Earth's surface */ +#define SENSORS_MAGFIELD_EARTH_MIN \ + (30.0F) /**< Minimum magnetic field on Earth's surface */ +#define SENSORS_PRESSURE_SEALEVELHPA \ + (1013.25F) /**< Average sea level pressure is 1013.25 hPa */ +#define SENSORS_DPS_TO_RADS \ + (0.017453293F) /**< Degrees/s to rad/s multiplier \ + */ +#define SENSORS_RADS_TO_DPS \ + (57.29577793F) /**< Rad/s to degrees/s multiplier */ +#define SENSORS_GAUSS_TO_MICROTESLA \ + (100) /**< Gauss to micro-Tesla multiplier */ + +/** Sensor types */ +typedef enum { + SENSOR_TYPE_ACCELEROMETER = (1), /**< Gravity + linear acceleration */ + SENSOR_TYPE_MAGNETIC_FIELD = (2), + SENSOR_TYPE_ORIENTATION = (3), + SENSOR_TYPE_GYROSCOPE = (4), + SENSOR_TYPE_LIGHT = (5), + SENSOR_TYPE_PRESSURE = (6), + SENSOR_TYPE_PROXIMITY = (8), + SENSOR_TYPE_GRAVITY = (9), + SENSOR_TYPE_LINEAR_ACCELERATION = + (10), /**< Acceleration not including gravity */ + SENSOR_TYPE_ROTATION_VECTOR = (11), + SENSOR_TYPE_RELATIVE_HUMIDITY = (12), + SENSOR_TYPE_AMBIENT_TEMPERATURE = (13), + SENSOR_TYPE_OBJECT_TEMPERATURE = (14), + SENSOR_TYPE_VOLTAGE = (15), + SENSOR_TYPE_CURRENT = (16), + SENSOR_TYPE_COLOR = (17), + SENSOR_TYPE_TVOC = (18), + SENSOR_TYPE_VOC_INDEX = (19), + SENSOR_TYPE_NOX_INDEX = (20), + SENSOR_TYPE_CO2 = (21), + SENSOR_TYPE_ECO2 = (22), + SENSOR_TYPE_PM10_STD = (23), + SENSOR_TYPE_PM25_STD = (24), + SENSOR_TYPE_PM100_STD = (25), + SENSOR_TYPE_PM10_ENV = (26), + SENSOR_TYPE_PM25_ENV = (27), + SENSOR_TYPE_PM100_ENV = (28), + SENSOR_TYPE_GAS_RESISTANCE = (29), + SENSOR_TYPE_UNITLESS_PERCENT = (30), + SENSOR_TYPE_ALTITUDE = (31) +} sensors_type_t; + +/** struct sensors_vec_s is used to return a vector in a common format. */ +typedef struct { + union { + float v[3]; ///< 3D vector elements + struct { + float x; ///< X component of vector + float y; ///< Y component of vector + float z; ///< Z component of vector + }; ///< Struct for holding XYZ component + /* Orientation sensors */ + struct { + float roll; /**< Rotation around the longitudinal axis (the plane body, 'X + axis'). Roll is positive and increasing when moving + downward. -90 degrees <= roll <= 90 degrees */ + float pitch; /**< Rotation around the lateral axis (the wing span, 'Y + axis'). Pitch is positive and increasing when moving + upwards. -180 degrees <= pitch <= 180 degrees) */ + float heading; /**< Angle between the longitudinal axis (the plane body) + and magnetic north, measured clockwise when viewing from + the top of the device. 0-359 degrees */ + }; ///< Struct for holding roll/pitch/heading + }; ///< Union that can hold 3D vector array, XYZ components or + ///< roll/pitch/heading + int8_t status; ///< Status byte + uint8_t reserved[3]; ///< Reserved +} sensors_vec_t; + +/** struct sensors_color_s is used to return color data in a common format. */ +typedef struct { + union { + float c[3]; ///< Raw 3-element data + /* RGB color space */ + struct { + float r; /**< Red component */ + float g; /**< Green component */ + float b; /**< Blue component */ + }; ///< RGB data in floating point notation + }; ///< Union of various ways to describe RGB colorspace + uint32_t rgba; /**< 24-bit RGBA value */ +} sensors_color_t; + +/* Sensor event (36 bytes) */ +/** struct sensor_event_s is used to provide a single sensor event in a common + * format. */ +typedef struct { + int32_t version; /**< must be sizeof(struct sensors_event_t) */ + int32_t sensor_id; /**< unique sensor identifier */ + int32_t type; /**< sensor type */ + int32_t reserved0; /**< reserved */ + int32_t timestamp; /**< time is in milliseconds */ + union { + float data[4]; ///< Raw data */ + sensors_vec_t acceleration; /**< acceleration values are in meter per second + per second (m/s^2) */ + sensors_vec_t + magnetic; /**< magnetic vector values are in micro-Tesla (uT) */ + sensors_vec_t orientation; /**< orientation values are in degrees */ + sensors_vec_t gyro; /**< gyroscope values are in rad/s */ + float temperature; /**< temperature is in degrees centigrade (Celsius) */ + float distance; /**< distance in centimeters */ + float light; /**< light in SI lux units */ + float pressure; /**< pressure in hectopascal (hPa) */ + float relative_humidity; /**< relative humidity in percent */ + float current; /**< current in milliamps (mA) */ + float voltage; /**< voltage in volts (V) */ + float tvoc; /**< Total Volatile Organic Compounds, in ppb */ + float voc_index; /**< VOC (Volatile Organic Compound) index where 100 is + normal (unitless) */ + float nox_index; /**< NOx (Nitrogen Oxides) index where 100 is normal + (unitless) */ + float CO2; /**< Measured CO2 in parts per million (ppm) */ + float eCO2; /**< equivalent/estimated CO2 in parts per million (ppm + estimated from some other measurement) */ + float pm10_std; /**< Standard Particulate Matter <=1.0 in parts per million + (ppm) */ + float pm25_std; /**< Standard Particulate Matter <=2.5 in parts per million + (ppm) */ + float pm100_std; /**< Standard Particulate Matter <=10.0 in parts per + million (ppm) */ + float pm10_env; /**< Environmental Particulate Matter <=1.0 in parts per + million (ppm) */ + float pm25_env; /**< Environmental Particulate Matter <=2.5 in parts per + million (ppm) */ + float pm100_env; /**< Environmental Particulate Matter <=10.0 in parts per + million (ppm) */ + float gas_resistance; /**< Proportional to the amount of VOC particles in + the air (Ohms) */ + float unitless_percent; /** +#include +#include + +#include "i2cmaster.hpp" + +I2cMaster::I2cMaster () { + address = 0; + timer = 0; +} + +void I2cMaster::tick1ms () { + if (timer > 0) { + timer--; + } +} + +bool I2cMaster::begin (uint8_t addr) { + this->address = addr; + // TWBR = 13; // 100kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 100000) / (2 * 100000 * 4); + TWBR = 100; // 50kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 50000) / (2 * 50000 * 4); + TWCR = (1 << TWEN); + return true; +} + +void I2cMaster::end () { + TWCR = (1 << TWEN); + TWBR = 0; +} + +bool I2cMaster::read (uint8_t *buffer, uint8_t len) { + if (start(true)) { + if (readBytes(buffer, len)) { + if (stop()) { + return true; + } + } + } + return false; +} + + +bool I2cMaster::write (const uint8_t *buffer, uint8_t len) { + if (start(false)) { + if (writeBytes(buffer, len, false)) { + if (stop()) { + return true; + } + } + } + return false; +} + +bool I2cMaster::write_P (const uint8_t *buffer, uint8_t len) { + if (start(false)) { + if (writeBytes(buffer, len, true)) { + if (stop()) { + return true; + } + } + } + return false; +} + + +bool I2cMaster::writeByteAndBuffer (uint8_t byte, const uint8_t *buffer, uint8_t len) { + if (start(false)) { + do { + TWDR = byte; + TWCR = (1 << TWINT) | (1 << TWEN); // send byte + while (!(TWCR & (1 << TWINT))) {}; // wait until last action done + if ((TWSR & 0xf8) != 0x28) { + return false; + } + byte = *buffer++; + } while (len-- > 0); + return true; + } + return false; +} + + +bool I2cMaster::write_then_read (const uint8_t *write_buffer, uint8_t write_len, uint8_t *read_buffer, uint8_t read_len) { + if (start(false)) { + if (writeBytes(write_buffer, write_len, false)) { + if (start(true)) { + if (readBytes(read_buffer, read_len)) { + if (stop()) { + return true; + } + } + } + } + } + return false; +} + +// ------------------------------------------------------------- + +bool I2cMaster::readBytes (uint8_t *buffer, uint8_t len) { + while (len-- > 0) { + if (len > 0) { + TWCR = (1 << TWEA) | (1 << TWINT) | (1 << TWEN); // read data byte with ACK enabled + } else { + TWCR = (1 << TWINT) | (1 << TWEN); // read data byte with ACK disabled + } + while (!(TWCR & (1 << TWINT))) {}; // wait until last action done + uint8_t sr = TWSR & 0xf8; + if ((len > 0 && sr != 0x50) || (len == 0 && sr != 0x58)) { + return false; + } + *buffer++ = TWDR; + } + return true; +} + +bool I2cMaster::writeBytes (const uint8_t *buffer, uint8_t len, bool fromFlash) { + while (len-- > 0) { + // printf_P(PSTR("[wB:len=%d, byte=%02x]"), len + 1, *buffer); + TWDR = fromFlash ? pgm_read_byte(buffer++) : *buffer++; + TWCR = (1 << TWINT) | (1 << TWEN); // send data byte + timer = 5; + while (timer > 0 && !(TWCR & (1 << TWINT))) {}; // wait until last action done + if (!timer || (TWSR & 0xf8) != 0x28) { + return false; + } + } + return true; +} + +bool I2cMaster::start (bool read) { + TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); // send START condition + timer = 5; + while (timer > 0 && !(TWCR & (1 << TWINT))) {}; // wait until last action done + uint8_t sr = TWSR & 0xf8; + if (!timer || (sr != 0x08 && sr != 0x10)) { + return false; + } + TWDR = (address << 1) | (read ? 1 : 0); // address + R/nW + TWCR = (1 << TWINT) | (1 << TWEN); // send address/RW + timer = 5; + while (timer > 0 && !(TWCR & (1 << TWINT))) {}; // wait until last action done + sr = TWSR & 0xf8; + if (!timer || (!read && sr != 0x18) || (read && sr != 0x40)) { + return false; + } + return true; +} + +bool I2cMaster::stop () { + TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO); + while (TWCR & ( 1 << TWSTO)); + return true; +} + diff --git a/software/deye-sun-12k/src/i2cmaster.hpp b/software/deye-sun-12k/src/i2cmaster.hpp new file mode 100644 index 0000000..89b1e46 --- /dev/null +++ b/software/deye-sun-12k/src/i2cmaster.hpp @@ -0,0 +1,30 @@ +#ifndef I2C_MASTER +#define I2C_MASTER + +#include + +class I2cMaster { + public: + static void end (); + + public: + I2cMaster (); + void tick1ms (); + bool begin (uint8_t addr); + bool read (uint8_t *buffer, uint8_t len); + bool write (const uint8_t *buffer, uint8_t len); + bool write_P (const uint8_t *buffer, uint8_t len); + bool write_then_read (const uint8_t *write_buffer, uint8_t write_len, uint8_t *read_buffer, uint8_t read_len); + bool writeByteAndBuffer (uint8_t byte, const uint8_t *buffer, uint8_t len); + + private: + uint8_t address; + uint8_t timer; + bool start (bool read); + bool stop (); + bool writeBytes (const uint8_t *buffer, uint8_t len, bool fromFlash); + bool writeBytes_P (const uint8_t *buffer); + bool readBytes (uint8_t *buffer, uint8_t len); +}; + +#endif \ No newline at end of file diff --git a/software/deye-sun-12k/src/i2cslave.cpp b/software/deye-sun-12k/src/i2cslave.cpp new file mode 100644 index 0000000..2bf6ac2 --- /dev/null +++ b/software/deye-sun-12k/src/i2cslave.cpp @@ -0,0 +1,92 @@ +#include +#include +#include + +#include "i2cslave.hpp" + +I2cSlave::I2cSlave () { + timer = 0; + fromMaster.rIndex = 0; + fromMaster.wIndex = 0; + toMaster.rIndex = 0; + toMaster.wIndex = 0; +} + +void I2cSlave::tick1ms () { + if (timer > 0) { + timer--; + } +} + +bool I2cSlave::begin (uint8_t addr, bool acceptGeneralCalls) { + if (addr > 127) { + return false; + } + TWAR = addr << 1 | (acceptGeneralCalls ? 1 : 0); + TWBR = 100; // 50kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 50000) / (2 * 50000 * 4); + TWCR = (1 << TWEA) | (1 << TWEN) | (1 << TWIE); + return true; +} + +void I2cSlave::end () { + TWCR = (1 << TWEN); + TWBR = 0; +} + +int I2cSlave::read () { + return getByte(fromMaster); +} + +void I2cSlave::write (uint8_t byte) { + putByte(toMaster, byte); +} + + +void I2cSlave::putByte (RingBuffer& buffer, uint8_t byte) { + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + buffer.data[buffer.wIndex++] = byte; + if (buffer.wIndex >= sizeof(buffer.data)) { + buffer.wIndex = 0; + } + if (buffer.wIndex == buffer.rIndex) { + buffer.rIndex++; + if (buffer.rIndex >= sizeof(buffer.data)) { + buffer.rIndex = 0; + } + } + } +} + +int I2cSlave::getByte (RingBuffer& buffer) { + uint8_t b; + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + if (buffer.rIndex == buffer.wIndex) { + return EOF; + } + b = buffer.data[buffer.rIndex++]; + if (buffer.rIndex >= sizeof(buffer.data)) { + buffer.rIndex = 0; + } + } + return b; +} + +void I2cSlave::handleTWIIsr () { + uint8_t sr = TWSR & 0xf8; + switch (sr) { + case 0x80: { // Previously addressed with own SLA+W; data has been received; ACK has been returned + putByte(fromMaster, TWDR); + TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWIE); // no TWEA -> only one byte accepted + break; + } + case 0xa8: { // Own SLA+R has been received; ACK has been returned + int response = getByte(toMaster);; + TWDR = response < 0 ? 0x00 : (uint8_t)response; + TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWIE); // no TWEA -> only one byte accepted + break; + } + default: TWCR = (1 << TWINT) | (1 << TWEA) | (1 << TWEN) | (1 << TWIE); break; + } + +} + diff --git a/software/deye-sun-12k/src/i2cslave.hpp b/software/deye-sun-12k/src/i2cslave.hpp new file mode 100644 index 0000000..2fe2dc7 --- /dev/null +++ b/software/deye-sun-12k/src/i2cslave.hpp @@ -0,0 +1,32 @@ +#ifndef I2C_SLAVE +#define I2C_SLAVE + +#include + +class I2cSlave { + private: + typedef struct { + uint8_t rIndex; + uint8_t wIndex; + uint8_t data[8]; + } RingBuffer; + + public: + I2cSlave (); + void tick1ms (); + bool begin (uint8_t addr, bool acceptGeneralCalls); + void end (); + void handleTWIIsr (); + int read (); + void write (uint8_t byte); + + private: + uint8_t timer; + RingBuffer fromMaster; + RingBuffer toMaster; + void putByte (RingBuffer& buffer, uint8_t byte); + int getByte (RingBuffer& buffer); + +}; + +#endif \ No newline at end of file diff --git a/software/deye-sun-12k/src/main.cpp b/software/deye-sun-12k/src/main.cpp new file mode 100644 index 0000000..8039aaf --- /dev/null +++ b/software/deye-sun-12k/src/main.cpp @@ -0,0 +1,458 @@ +#include +#include +#include +#include + +#include +#include + +#include "main.hpp" +#include "units/encoder.hpp" +#include "units/i2c.hpp" +#include "units/led.hpp" +#include "units/ieee485.hpp" +#include "units/led.hpp" +#include "units/lcd.hpp" +#include "units/switch.hpp" +#include "units/rgb.hpp" +#include "units/seg7.hpp" +#include "units/poti.hpp" +#include "units/r2r.hpp" +#include "units/motor.hpp" +#include "units/portexp.hpp" +#include "units/uart1.hpp" +#include "units/modbus.hpp" +#include "units/rtc8563.hpp" +#include "units/cc1101.hpp" +#include "units/deye.hpp" + + +const char MAIN_CPP_DATE[] PROGMEM = __DATE__; +const char MAIN_CPP_TIME[] PROGMEM = __TIME__; +#ifdef __AVR_ATmega328P__ + const char MAIN_CPP_PART_NAME[] PROGMEM = "ATmega328P"; +#endif +#ifdef __AVR_ATmega644P__ + const char MAIN_CPP_PART_NAME[] PROGMEM = "ATmega644P"; +#endif +#ifdef __AVR_ATmega1284P__ + const char MAIN_CPP_PART_NAME[] PROGMEM = "ATmega1284P"; +#endif + +#ifndef F_CPU + #if defined(__AVR_ATmega328P__) + #define F_CPU 16000000L + #define BAUD_RATE 38400 + #elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__) + #define F_CPU 12000000L + #define BAUD_RATE 115200 + #else + #error missing defined for F_CPU + #endif +#endif + +#ifndef BAUD_RATE + #if defined(__AVR_ATmega328P__) + #define BAUD_RATE 38400 + #elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__) + #define BAUD_RATE 115200 + #else + #error missing define BAUD_RATE + #endif +#endif + +const char PSTR_DIVIDER[] PROGMEM = "\n=====================================\n "; +const char PSTR_LINEFEED[] PROGMEM = "\n"; +const char PSTR_ERROR[] PROGMEM = "ERROR"; +const char PSTR_Done[] PROGMEM = "Done"; +// const char PSTR_HARDWARE_V1a[] PROGMEM = "V1a"; +// const char PSTR_HARDWARE_V2a[] PROGMEM = "V2a"; +// const char* const PSTR_HARDWARE[] PROGMEM = { PSTR_HARDWARE_V1a, PSTR_HARDWARE_V2a }; + +uint8_t hardwareVersion = 0; + +extern "C" { + void __cxa_pure_virtual () {} + int __cxa_guard_acquire(uint8_t *g) { return 0; } + void __cxa_guard_release(uint8_t *g) {} + void __cxa_guard_abort(uint8_t *g) {} + void __gxx_personality_sj0 () {} + void __cxa_rethrow () {} + void __cxa_begin_catch () {} + void __cxa_end_catch () {} + + int uart_putchar(char c, FILE *stream) { + if (c == '\n') { + uart_putchar('\r', stream); + } + if (stream == stdout) { + loop_until_bit_is_set(UCSR0A, UDRE0); + UDR0 = c; + } + return 0; + } + + + uint64_t volatile systemMillis = 0; + uint8_t volatile uartBuffer[32]; + uint8_t volatile rIndex = 0; + uint8_t volatile wIndex = 0; + + int uart_getchar (FILE *stream) { + // if (rIndex == wIndex) { + // // nothing in buffer + // return EOF; + // } + // printf_P(PSTR(" r%d"), rIndex); + while (rIndex == wIndex) { + // wait for character + } + + // don't use "char c" because german special characters would lead to negative return -> stream error + // char c = uartBuffer[rIndex++]; + + uint8_t c = uartBuffer[rIndex++]; + // printf_P(PSTR("(%02x) "), c); + if (c == '\r') { + c = '\n'; + } + putchar(c); // echo on terminal + return c; + } + + static FILE mystdout = { 0, 0, _FDEV_SETUP_WRITE , 0, 0, uart_putchar, NULL, 0 }; + static FILE mystdin = { 0, 0, _FDEV_SETUP_READ , 0, 0, NULL, uart_getchar, 0 }; + + #if defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__) + static volatile uint16_t timerFlashGreenLed = 0; + static volatile uint16_t timerFlashRedLed = 0; + void flashRedLed (uint16_t ms) { + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + if (timerFlashRedLed == 0) { + PORTC |= (1 << PC2); + if (ms < 0xff80) { + ms += 0x80; // at least 128ms OFF after + } + timerFlashRedLed = ms; + } + } + } + void flashGreenLed (uint16_t ms) { + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + if (timerFlashGreenLed == 0) { + PORTC |= (1 << PC4); + if (ms < 0xff80) { + ms += 0x80; // at least 128ms OFF after + } + timerFlashGreenLed = ms; + } + } + } + #endif + + static volatile uint32_t timer1ms = 0; + static volatile int keyUart0 = EOF; + + Led led; + Switch sw; + Rgb rgb; + Seg7 seg7; + Poti poti; + Encoder encoder; + R2r r2r; + Motor motor; + PortExp portExp; + Lcd lcd; + Uart1 uart1; + Modbus modbus; + Ieee485 ieee485; + I2c i2cSparkfun(I2c::SparkFunEnvCombo); + I2c i2cMaster(I2c::Master); + I2c i2cSlave(I2c::Slave); + Rtc8563 rtc8563(Rtc8563::NORMAL); + Cc1101 cc1101Send(Cc1101::Send); + Cc1101 cc1101Receive(Cc1101::Receive); + Deye deye; +} + +const char *hardwareVersionPStr (uint8_t version) { + switch (version) { + case 1: return PSTR("V1a"); + case 2: return PSTR("V2a"); + default: return PSTR("V??"); + } +} + +uint8_t detectHardwareVersion () { + ADMUX = (1 << ADLAR) | (1 << REFS0) | 7; // ADC7, VREF=AVCC=3.3V + ADCSRA = (1 << ADEN) | 7; // ADC Enable, Prescaler 128 + ADCSRA |= (1 << ADSC); // start ADC + while (ADCSRA & (1 << ADSC)) {} // wait for result + hardwareVersion = 0; // unknown board version + // printf("Hardware-Version: ADC7H = %d\n", ADCH); + #if defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__) + + if (ADCH >= 0xf4) { + hardwareVersion = 1; + } else if (ADCH >= 0xe0) { + hardwareVersion = 2; + } + #endif + #if defined(__AVR_ATmega328P__) + if (ADCH < 0xd8 && ADCH >= 0xc0) { + hardwareVersion = 1; + } else if (ADCH < 0xd8 && ADCH >= 0xb0) { + hardwareVersion = 2; + } + #endif + + if (hardwareVersion < 1 || hardwareVersion > 2) { + #ifdef __AVR_ATmega644P__ + printf_P(PSTR("\nInvalid Nano-X-Base Hardware-Version: ADC7H = %d (ATmega644P, 3.3V)\n"), ADCH); + #elif __AVR_ATmega1284P__ + printf_P(PSTR("\nInvalid Nano-X-Base Hardware-Version: ADC7H = %d (ATmega1284P, 3.3V)\n"), ADCH); + #elif __AVR_ATmega328P__ + printf_P(PSTR("\nInvalid Nano-X-Base Hardware-Version: ADC7H = %d (ATmega328P, 5V)\n"), ADCH); + #endif + hardwareVersion = 0; + } else { + // const char *pHw; memcpy_P(&pHw, PSTR_HARDWARE + hardwareVersion - 1, sizeof(const char *)); + printf_P(PSTR("\n\nHardware %S detected (ADC7H=0x%02X)"), hardwareVersionPStr(hardwareVersion), ADCH); + } + + + ADMUX = 0; + ADCSRA = 0; + return hardwareVersion; +} + +void setTimer (uint32_t ms) { + ATOMIC_BLOCK(ATOMIC_FORCEON) { + timer1ms = ms; + } +} + +int wait (uint32_t ms) { + setTimer(ms); + do { + ATOMIC_BLOCK(ATOMIC_FORCEON) { + ms = timer1ms; + } + } while (ms > 0 && keyUart0 == EOF); + return keyUart0; +} + +int waitAndReadKey (uint32_t ms) { + keyUart0 = EOF; + int key = wait(ms); + keyUart0 = EOF; + return key; +} + +int main () { + + #if defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__) + // Nano-644 LEDs (Green, Orange, Red) + DDRC |= (1 << PC7) | (1 << PC4) | (1 << PC3) | (1 << PC2); + PORTC &= ~((1 << PC7) | (1 << PC4) | (1 << PC3) | (1 << PC2)); + + // Nano-644 push button SW2 + DDRC &= ~(1 << PC5); + PORTC |= (1 << PC5); // enable internal pullup resistor + #endif + + #ifdef __AVR_ATmega328P__ + DDRB |= (1 << PB5); + PORTB &= ~(1 << PB5); + #endif + + // UART0 interface on Nano-644 + UCSR0A = (1 << U2X0); + UCSR0B = (1 << RXCIE0) | (1 << RXEN0) | (1 <= 1 && hardwareVersion <= 2) { + printf_P(PSTR("Nano-X-Base: %S"), hardwareVersionPStr(hardwareVersion)); + } else { + printf_P(PSTR("No Nano-X-Base detected")); + } + printf_P(PSTR_DIVIDER); + printf_P(PSTR_LINEFEED); + printf_P(PSTR("Available units:\n\n")); + for (i = 0; i < units; i++) { + TestUnit *pu = unit[i]; + printf_P(PSTR("%3x ... "), i); + printf_P(pu->getName()); + printf_P(PSTR_LINEFEED); + } + printf_P(PSTR("\nSelect unit: ")); + rIndex = 0; wIndex = 0; + fgets(s, sizeof(s), stdin); + } while (sscanf(s, "%x", &i) != 1 || i < 0 || i >= units ); + + TestUnit *pu = unit[i]; + printf_P(PSTR("\n\n[")); printf_P(pu->getName()); printf_P(PSTR("]: ")); + keyUart0 = EOF; + + pu->init(); + for (uint8_t subtest = 0; subtest < 0xff; subtest++) { + printf_P(PSTR("\n%4d: "), subtest); + if (pu->run(subtest) < 0) { + break; + } + if (keyUart0 == 27) { + keyUart0 = EOF; + break; + } + keyUart0 = EOF; + } + pu->cleanup(); + } +} + +uint64_t millis () { + volatile uint64_t millis = systemMillis; + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + millis = systemMillis; + } + return millis; +} + +#ifndef USART0_RX_vect + #define USART0_RX_vect USART_RX_vect +#endif +ISR (USART0_RX_vect) { + uint8_t b = UDR0; + keyUart0 = b; + uartBuffer[wIndex++] = b; + // printf_P(PSTR(" w%d(%02x)"), wIndex, b); + if (wIndex == rIndex) { + // buffer overflow, kick out oldest byte + rIndex++; + } +} + +#ifdef USART1_RX_vect + ISR (USART1_RX_vect) { + uint8_t b = UDR1; + if (modbus.enabled) { + modbus.handleRxByte(b); + } + if (deye.enabled) { + deye.handleRxByte(b); + } + if (uart1.enabled) { + uart1.handleRxByte(b); + } + if (ieee485.enabled) { + ieee485.handleRxByte(b); + } + } +#endif + +ISR (TWI_vect) { + if (i2cMaster.enabled) { + i2cMaster.handleTwiIrq(); + } else if (i2cSlave.enabled) { + i2cSlave.handleTwiIrq(); + } else if (i2cSparkfun.enabled) { + i2cSparkfun.handleTwiIrq(); + } else { + TWCR = (1 << TWINT); // clear interrupt request bit and disable TWI + } +} + +ISR (TIMER2_COMPA_vect) { // every 100us + static uint16_t timer500ms = 0; + static uint8_t timer100us = 0; + + if (encoder.enabled) { + encoder.tick100us(); + } + if (motor.enabled) { + motor.tick100us(); + } + + timer100us++; + if (timer100us >= 10) { + timer100us = 0; + if (timer1ms > 0) { + timer1ms--; + } + systemMillis++; + i2cMaster.tick1ms(); + i2cSlave.tick1ms(); + i2cSparkfun.tick1ms(); + cc1101Send.tick1ms(); + cc1101Receive.tick1ms(); + #if defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__) + if (timerFlashRedLed > 0) { + if (--timerFlashRedLed < 128) { + PORTC &= ~(1 << PC2); // red LED + } + } + if (timerFlashGreenLed > 0) { + if (--timerFlashGreenLed < 128 ) { + PORTC &= ~(1 << PC4); // green LED + } + } + #endif + } + + timer500ms++; + if (timer500ms >= 5000) { + #if defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__) + PORTC ^= (1 << PC3); // orange LED blinking + #endif + #ifdef __AVR_ATmega328P__ + if (!seg7.enabled) { + PORTB ^= (1 << PB5); // LED L + } + #endif + timer500ms = 0; + } +} + diff --git a/software/deye-sun-12k/src/main.hpp b/software/deye-sun-12k/src/main.hpp new file mode 100644 index 0000000..7eeff16 --- /dev/null +++ b/software/deye-sun-12k/src/main.hpp @@ -0,0 +1,38 @@ +#ifndef MAIN_HPP +#define MAIN_HPP + +#include +#include + +#define ENTER '\r' +#define CTRLC '\003' +#define ESCAPE 0x1b + +extern int wait (uint32_t ms); +extern int waitAndReadKey (uint32_t ms); +extern uint64_t millis (); + +extern uint8_t hardwareVersion; + +extern const char PSTR_DIVIDER[]; +extern const char PSTR_LINEFEED[]; +extern const char PSTR_ERROR[]; +extern const char PSTR_Done[]; + +#if defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__) + extern "C" { + extern void flashRedLed (uint16_t ms); + extern void flashGreenLed (uint16_t ms); + } +#endif + + +class TestUnit { + public: + virtual int8_t run (uint8_t subtest) = 0; + virtual void init () = 0; + virtual void cleanup () = 0; + virtual PGM_P getName () = 0; +}; + +#endif \ No newline at end of file diff --git a/software/deye-sun-12k/src/units/cc1101.cpp b/software/deye-sun-12k/src/units/cc1101.cpp new file mode 100644 index 0000000..6cb011e --- /dev/null +++ b/software/deye-sun-12k/src/units/cc1101.cpp @@ -0,0 +1,761 @@ +#include +#include +#include +#include + +#include "cc1101.hpp" +#include "../main.hpp" + +// 868MHz Modem E07-900MM1DS (chipset CC1101) +// https://jlcpcb.com/partdetail/Chengdu_Ebyte_ElecTech-E07900MM10S/C5844212 + +#ifdef __AVR_ATmega328P__ + + // Arduino-Nano-5V + // ------------------------------------ + // not available + + void Cc1101::init () {} + void Cc1101::cleanup () {} + void Cc1101::setChipEnableLow () {} + void Cc1101::setChipEnableHigh () {} + int8_t Cc1101::run (uint8_t subtest) { return -1; } + PGM_P Cc1101::getName () { return PSTR("?"); } + +#endif + + +#if defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__) + + // Nano-644 + // -------------------------------------------------------- + // PA4 ... nCS + // PB4 ... nSS (not used, but must be high, otherwise AVR SPI master not working) + // PB5 ... MOSI + // PB6 ... MISO + // PB7 ... SCK + + // -------------------------------------------------------- + + // Output power table from CC1101 datasheet page 59 + const Cc1101::PATABLE_INIT_ITEM_t PMEM_CC1101_PATABLE_INIT[] PROGMEM = { + { 0xc6, "9.6" }, // 9.6 dBm / 29.4mA (default value) + { 0xc0, "11" }, // 11 dBm / 34.2mA + { 0xc5, "10" }, // 10 dBm / 30.0mA + { 0xcd, "7" }, // 7 dBm / 25.8mA + { 0x86, "5" }, // 5 dBm / 19.9mA + { 0x50, "0" }, // 0 dBm / 16.8mA + { 0x37, "-6" }, // -6 dBm / 16.4mA + { 0x26, "-10" }, // 10 dBm / 14.5mA + { 0x1d, "-15" }, // 15 dBm / 13.3mA + { 0x17, "-20" }, // 20 dBm / 12.6mA + { 0x03, "-30" }, // 30 dBm / 12.0mA + }; + const Cc1101::Register_t PMEM_CC1101_REGISTER_INIT PROGMEM = { + /* 0x00: iocfg2 0x0b (----) */ { Cc1101::CHIP_NOT_READY, 0, 0 }, // GDO2 + /* 0x01: iocfg1 0x2e (----) */ { Cc1101::HIGH_IMPEDANCE_WHEN_CHIP_SELECT_HIGH, 0, 0 }, // GDO1/MISO + /* 0x02: iocfg0 0x06 (----) */ { Cc1101::SYNC_SENT_OR_RECEIVED_UNTIL_END_OF_PACKET, 0, 0 }, // GDO0 -> PA5 + /* 0x03: fifothr 0x47 (0x07) */ { 7, 0, 1, 0 }, // fifo_thr = 0b111, close_in_rx=0, adc_retention = 1, (bit7=0) + /* 0x04: sync1 0x7a (0xd3) */ 0x7a, + /* 0x05: sync0 0x0e (0x91) */ 0x0e, + /* 0x06: pktlen 0x14 (0x3d) */ PACKET_LENGTH, + /* 0x07: pktctrl1 0x04 (----) */ { Cc1101::NO_ADDR_CHECK, 1, 0, 0, 0 }, // adr_chk=0b00, append_status=1, crc_autoflush=0, (bit4=0), pqt=0b000 + /* 0x08: pktctrl0 0x05 (----) */ { Cc1101::FIXED, 1, 0, Cc1101::NORMAL_USE_FIFO, 0, 0 }, // length_config=0b01, crc_en=1, (bit3=0), pkt_format=0b00, white_data=0, (bit7=0) + /* 0x09: addr 0x00 (----) */ 0x00, + /* 0x0a: channr 0x00 (----) */ 0x00, + /* 0x0b: fsctrl1 0x06 (0x08) */ { 6, 0, 0 }, // frqu_if=6, (bit5=0), (bit76=0b00) + /* 0x0c: fsctrl0 0x00 (----) */ { 0 }, // freqoff = 0 + /* 0x0d: frequ2 0x21 (----) */ 0x21, + /* 0x0e: frequ1 0x62 (----) */ 0x62, + /* 0x0f: frequ0 0x76 (----) */ 0x76, + /* 0x10: mdmcfg4 0xca (0x5b) */ { 0x0a, 0, 3 }, // drate_e=0x0a, chanbw_m=0, chanbw_e=3 + /* 0x11: mdmcfg3 0xf8 (----) */ { 0xf8 }, // drate_m=0xf8 + /* 0x12: mdmcfg2 0x16 (0x03) */ { Cc1101::SYNC_16_16_CARRIER_SENSE, 0, Cc1101::GFSK, 0}, // sync_mode=0b110, manchester_en=0, mod_format=0b001, dem_dcfilt_off=0 + /* 0x13: mdmcfg1 0x22 (----) */ { 2, 0, Cc1101::FOUR, 0 }, // chanspc_e=0b10, bit32=0, num_preamble=0b010, fec_en=0 + /* 0x14: mdmcfg0 0xf8 (----) */ { 0xf8 }, // chanspc_m = 0x08 + /* 0x15: deviatn 0x40 (0x47) */ { 0, 0, 4, 0 }, // deviation_m=0, (bit3=0), deviation_e=4, (bit7=0) + /* 0x16: mcsm2 0x07 (----) */ { 7, 0, 0, 0 }, // rx_time=7 (NA), rx_time_qual=0, rx_time_rssi=0, (bit76=0b00) + /* 0x17: mcsm1 0x30 (----) */ { Cc1101::TXOFF_IDLE, Cc1101::RXOFF_IDLE, Cc1101::RSSI_BELOW_THRESHOLD__UNLESS_RECEIVE_PACKET, 0 }, // mcsm1 (txoff_mode=0b00, rxoff_mode=0b00, cca_mode=0b11, (bit76=0b00) ) + /* 0x18: mcsm0 0x18 (----) */ { 0, 0, 2, Cc1101::IDLE_TO_RX_OR_TX, 0 }, // xosc_force_on=0, pin_ctrl_en=0, po_timeout=2 (149-155us), fs_autocal=0b01, (bit76=0b00) + /* 0x19: foccfg 0x16 (0x1d) */ 0x16, + /* 0x1a: bscfg 0x6c (0x1c) */ 0x6c, + /* 0x1b: agcctrl2 0x43 (0xc7) */ 0x43, + /* 0x1c: agcctrl1 0x49 (0x00) */ 0x49, + /* 0x1d: agcctrl0 0x91 (0xb2) */ 0x91, + /* 0x1e: worevt1 0x87 (----) */ 0x87, + /* 0x1f: worevt0 0x6b (----) */ 0x6b, + /* 0x20: worctrl 0xfb (0xf8) */ 0xfb, + /* 0x21: frend1 0x56 (0xb6) */ 0x56, + /* 0x22: frend0 0x10 (----) */ { 0, 0, 1, 0 }, // pa_power = 0, (bit3 = 0), lodiv_buf_current = 1, (bit76=0b00) + /* 0x23: fscal3 0xe9 (0xea) */ 0xe9, + /* 0x24: fscal2 0x2a (----) */ 0x2a, + /* 0x25: fscal1 0x00 (----) */ 0x00, + /* 0x26: fscal0 0x1f (0x11) */ 0x1f, + /* 0x27: rcctrl1 0x41 (----) */ 0x41, + /* 0x28: rcctrl0 0x00 (----) */ 0x00, + /* 0x29: fstest 0x59 (----) */ 0x59, + /* 0x2a: ptest 0x7f (----) */ 0x7f, + /* 0x2b: agctest 0x3f (----) */ 0x3f, + /* 0x2c: test2 0x81 (0x88) */ 0x81, + /* 0x2d: test1 0x35 (0x31) */ 0x35, + /* 0x2e: test0 0x09 (0x0b) */ 0x09 + }; + + // -------------------------------------------------------------------------------------- + + int8_t Cc1101::runSend (uint8_t subtest) { + if (subtest == 0) { + bool ok = true; + GDOx_CFG_t cfg0 = SYNC_SENT_OR_RECEIVED_UNTIL_END_OF_PACKET; + ok &= writeRegister(0x02, cfg0); + return ok; + } else if (subtest > 1) { + return -1; + } + + printf_P(PSTR(" use + and - for power change (other key -> back to default)")); + + uint16_t cnt = 0; + uint8_t paIndex = 0; + uint8_t pa_power; + int key = EOF; + do { + // if ((cnt & 0x0f) == 0) { + // paIndex = (cnt >> 4) & 0x07; + // union { FREND0_t value; uint8_t byte; } frend0 = { paIndex, 0, 1, 0 }; + // printf_P(PSTR("\n switch power ")); + // if (writeRegister(FREND0, frend0.byte )) { + // printf_P(PSTR("to %d (FREND0 = 0x%02X)"), frend0.value.pa_power, frend0.byte); + // } else { + // printf_P(PSTR("fails")); + // } + // } + if (key != EOF) { + uint8_t max = (sizeof(PMEM_CC1101_PATABLE_INIT) / sizeof(PMEM_CC1101_PATABLE_INIT[0])) - 1; + switch (key) { + case '+': if (paIndex == 0) paIndex = 1; else if (paIndex > 1) paIndex--; break; + case '-': if (paIndex == 0) paIndex = max; else if (paIndex < max) paIndex++; break; + default: paIndex = 0; break; // default value + } + + const PATABLE_INIT_ITEM_t *values = PMEM_CC1101_PATABLE_INIT; + + memcpy_P(&pa_power, &((values[paIndex]).pa_power), 1); + PGM_P info = (values[paIndex]).dbm; + printf_P(PSTR("\n switch power to ")); printf_P(info); + printf_P(PSTR("dBm ")); + if (writeRegister(0x3e, pa_power )) { + printf_P(PSTR("(PATABLE = 0x%02X)"), pa_power); + } else { + printf_P(PSTR("fails")); + } + } + + MainRadioControlState state = UNKNOWN; + uint8_t data[PACKET_LENGTH]; + printf_P(PSTR("\n [%04x]: state="), cnt++); + if (!readStatusRegister(MARCSTATE, (uint8_t *)&state)) { + printf_P(PSTR("E1")); + } else { + printf_P(PSTR("0x%02x"), state); + } + data[0] = pa_power; + printf_P(PSTR(" --> send %d bytes (HEX: %02X"), sizeof(data), paIndex); + for (uint8_t i = 1; i < sizeof(data); i++) { + data[i] = uint8_t((cnt + i - 1) & 0xff); + printf_P(PSTR(" %02X"), data[i]); + } + printf_P(PSTR("] -> ")); + + if (!sendData(data, sizeof(data))) { + flashRedLed(100); + printf_P(PSTR("E1")); + continue; + } + flashGreenLed(100); + key = waitAndReadKey(2000); + printf_P(PSTR("OK")); + + } while (key == EOF || (key != ESCAPE)); + + return 0; + } + + int8_t Cc1101::runReceive (uint8_t subtest) { + if (subtest == 0) { + bool ok = true; + GDOx_CFG_t cfg0 = SYNC_SENT_OR_RECEIVED_UNTIL_END_OF_PACKET; + ok &= writeRegister(0x02, cfg0); + return ok; + } else if (subtest > 1) { + return -1; + } + + int key = EOF; + uint16_t cnt = 0; + MainRadioControlState state; + while (key == EOF) { + printf_P(PSTR("\n [%04x] => start ... "), cnt++); + strobe(SRX); + do { + state = UNKNOWN; + if (!readStatusRegister(MARCSTATE, (uint8_t *)&state)) { + printf_P(PSTR("E1")); + _delay_ms(500); + break; + } + if (wait(0) != EOF) { + printf_P(PSTR("cancelled")); + _delay_ms(500); + break; + } + if (state == IDLE) { + printf_P(PSTR("? (IDLE)")); + _delay_ms(500); + break; + } + + } while (state != RX && state != RXFIFO_OVERFLOW); + + if (state != RX && state != RXFIFO_OVERFLOW) { + continue; + } + printf_P(PSTR("OK, receive ... ")); + + uint8_t length; + uint8_t lastLength = 0xff; + do { + uint8_t data[PACKET_LENGTH]; + if (!readStatusRegister(MARCSTATE, (uint8_t *)&state)) { + printf_P(PSTR("E2")); + _delay_ms(500); + continue; + } + if (wait(0) != EOF) { + printf_P(PSTR("cancelled")); + _delay_ms(500); + return -1; + } + if (!readStatusRegister(RXBYTES, (uint8_t *)&length)) { + printf_P(PSTR("E2")); + _delay_ms(500); + continue; + } + if (lastLength != length) { + lastLength = length; + } + if (length >= PACKET_LENGTH) { + if (receiveData(data, &length, sizeof(data))) { + if (length > 0) { + flashGreenLed(100); + printf_P(PSTR(" -> ")); + if (status.receivePacketStatusValid) { + printf_P(PSTR(" RSSI=%d, LQI=%d, CRC "), status.receivedPacketStatus.value.rssi, status.receivedPacketStatus.value.lqi); + if (status.receivedPacketStatus.value.crcOk) { + printf_P(PSTR("OK, ")); + } else { + printf_P(PSTR("ERROR, ")); + } + } + printf_P(PSTR("%d data bytes (HEX): "), length); + for (uint8_t i = 0; i < length; i++) { + printf_P(PSTR(" %02X"), data[i]); + } + printf_P(PSTR(" ... ")); + } + } + } + + } while (state == RX || state == RXFIFO_OVERFLOW); + + printf_P(PSTR("done (state=0x%02x)"), state); + } + return -1; + } + + int8_t Cc1101::runTest (uint8_t subtest) { + if (subtest > 0) { + return -1; + } + while (wait(0) == EOF) {} + return -1; + } + + + int8_t Cc1101::run (uint8_t subtest) { + if (!initDone) { + return -1; + } + switch (mode) { + case Send: return runSend(subtest); + case Receive: return runReceive(subtest); + case Test: return runTest(subtest); + default: return -1; + } + } + + uint8_t Cc1101::sendSpiByte (uint8_t b) { + SPDR = b; + while (!(SPSR & (1< "), addr, value); + if (ok && status.receivePacketStatusValid) { + printf_P(PSTR("status=0x%02x]"), status.spiResponse.byte); + } else { + printf_P(PSTR("ERR]")); + } + } + + return ok; + } + + bool Cc1101::writeRegisters (uint8_t addr, uint8_t *buffer, uint8_t length) { + bool ok = true; + uint8_t l = length; + uint8_t *p = buffer; + + addr = (addr & 0x3f) | 0x40; // write burst + setChipEnableLow(); + ok &= waitForMisoLow(); + sendSpiByte(addr); + while (length-- > 0) { + sendSpiByte(*buffer++); + } + setChipEnableHigh(); + + if (debugPrint.print.writeRegisters) { + printf_P(PSTR("\n [writeRegisters(0x%02x): "), addr & 0x3f); + while (l-- > 0) { + printf_P(PSTR(" 0x%02x"), *p++); + } + printf_P(PSTR(" -> ")); + if (ok && status.receivePacketStatusValid) { + printf_P(PSTR("status=0x%02x]"), status.spiResponse.byte); + } else { + printf_P(PSTR("ERR]")); + } + } + + return ok; + } + + + bool Cc1101::readRegister (uint8_t addr, uint8_t *value) { + bool ok = true; + addr = (addr & 0x3f) | 0x80; + setChipEnableLow(); + ok &= waitForMisoLow(); + status.spiResponse.byte = sendSpiByte(addr); + status.spiResponseValid = 1; + *value = sendSpiByte(0); + setChipEnableHigh(); + + if (debugPrint.print.readRegister) { + printf_P(PSTR("\n [readRegister(0x%02x) -> "), addr & 0x3f); + if (ok && status.spiResponseValid) { + printf_P(PSTR("0x%02x,status=0x%02x]"), *value, status.spiResponse.byte); + } else { + printf_P(PSTR("ERR]")); + } + } + return ok; + } + + bool Cc1101::readRegisters (uint8_t addr, uint8_t *buffer, uint8_t length) { + bool ok = true; + + uint8_t l = length; + uint8_t *p = buffer; + + addr |= 0xc0; // read burst + setChipEnableLow(); + ok &= waitForMisoLow(); + sendSpiByte(addr); + while (length-- > 0) { + *buffer++ = sendSpiByte(0); + } + setChipEnableHigh(); + + if (debugPrint.print.readRegisters) { + printf_P(PSTR("\n [readRegisters(0x%02x, ..., %d)] -> "), addr & 0x3f, l); + if (ok) { + while (l-- > 0) { + printf_P(PSTR(" 0x%02x"), *p++); + } + printf_P(PSTR("]")); + } else { + printf_P(PSTR("ERR]")); + } + } + + return ok; + } + + bool Cc1101::readStatusRegister (StatusAddress_t addr, uint8_t *value) { + if (addr < 0x30 || addr > 0x3d) { + return false; + } else { + return readRegisters(addr, value, 1); + } + } + + bool Cc1101::sendData (uint8_t *buffer, uint8_t length) { + timer = 10; + uint8_t ok = true; + uint8_t txbytes; + uint8_t states[16]; uint8_t statesIndex = 0; + // ok &= writeRegister(0x3f, length); + ok &= writeRegisters(0x3f, buffer, length); + ok &= readStatusRegister(TXBYTES, &txbytes); + ok &= (txbytes == 20); + if (ok) { + ok &= strobe(STX); // start sending bytes + // ok &= waitForGD0High(); // wait for start of SYNC + if (ok) { + uint8_t lastState = 0; + uint8_t state; + do { + ok &= readStatusRegister(MARCSTATE, &state); + if (state != lastState && statesIndex < 16) { + states[statesIndex++] = state; + } + lastState = state; + } while (ok && state != 0x13 && timer > 0); + ok &= state == 0x13; // check if in state TX + } + if (ok) { + uint8_t lastTxbytes = 20; + do { + ok &= readStatusRegister(TXBYTES, &txbytes); + if (lastTxbytes != txbytes) { + lastTxbytes = txbytes; + } + } while (ok && txbytes > 0 && timer > 0); + ok &= txbytes == 0; + } + } + if (!ok) { + strobe(SFTX); // flush TXfifo + } else { + uint8_t state; + uint8_t lastState = 0; + do { + ok &= readStatusRegister(MARCSTATE, &state); + if (lastState != state) { + lastState = state; + } + } while (ok && state != 0x01 && timer > 0); + ok &= state == 0x01; // check if in state IDLE + } + // ok &= waitForGD0Low(); // wait for end of package + + if (ok) { + printf_P(PSTR(" States[")); + for (uint8_t i = 0; i < statesIndex; i++) { + printf_P(PSTR(" 0x%02X"), states[i]); + } + printf_P(PSTR("] ")); + } + + return ok; + } + + bool Cc1101::receiveData (uint8_t *buffer, uint8_t *receivedLength, uint8_t maxBufferSize) { + bool ok = true; + STATUS_RXBYTES_t status; + uint8_t lastLength = 0; + uint8_t length = 0; + *receivedLength = 0; + timer = 50; + + this->status.receivePacketStatusValid = 0; + ok &= readStatusRegister(RXBYTES, &status.byte); + if (ok && status.rxbytes.num_rxbytes > 0) { + length = status.rxbytes.num_rxbytes; + + do { + _delay_us(400); // 20 Bytes in 4ms -> 200us/Byte -> CC1101 datasheet page 56: "twice that of which RF bytes are recived" + lastLength = length; + ok &= readStatusRegister(RXBYTES, &status.byte); + if (ok) { + // printf_P(PSTR(" [rxbytes=%d] "), status.byte); + length = status.byte & 0x7f; + } + ok &= timer > 0; + } while (ok && lastLength != length); + + if (ok) { + uint8_t extraBytesCount = this->status.receivePacketStatusEnabled ? 2 : 0; + if ((PACKET_LENGTH + extraBytesCount) != length) { + printf_P(PSTR(" ERROR[receive %d bytes, expect %d bytes] "), *receivedLength, PACKET_LENGTH + extraBytesCount); + *receivedLength = 0; + ok = false; + } else { + printf_P(PSTR(" OK[receive %d bytes] "), length); + *receivedLength = PACKET_LENGTH < maxBufferSize ? length - extraBytesCount : maxBufferSize; + ok &= readRegisters(0xff, buffer, *receivedLength); + if (!ok) { + *receivedLength = 0; + } else { + length -= *receivedLength; + if (length > extraBytesCount) { + printf_P(PSTR(" [WARN: buffer to small] ")); + while (length > extraBytesCount) { + uint8_t byte; + ok &= readRegister(0xff, &byte); + ok = false; + length--; + } + } + if (length > 0) { + ok &= readRegisters(0xff, this->status.receivedPacketStatus.byte, 2); + if (ok) { + this->status.receivePacketStatusValid = 1; + } + } + } + } + } + } + ok &= strobe(SFRX); + + return ok; + } + + + void Cc1101::printRegisters () { + const Register_t *regValues = &PMEM_CC1101_REGISTER_INIT; + printf_P(PSTR("\n")); + for (uint8_t addr = 0; addr < sizeof(Register_t); addr++) { + bool ok = true; + uint8_t regValue, value; + memcpy_P(®Value, ((const uint8_t *)regValues) + addr, 1); + ok &= readRegister(addr, &value); + if (value != regValue) { printf_P(PSTR(" != 0x%02x"), regValue); } + } + uint8_t data[8]; + for (uint8_t addr = 0x30; addr <= 0x3d; addr++) { + readStatusRegister((StatusAddress_t)addr, data); + } + readRegisters(0x3e, data, 8); // PATABLE + } + + bool Cc1101::resetCC1101 () { + bool ok = true; + setChipEnableLow(); + _delay_us(10); + setChipEnableHigh(); + _delay_us(50); + setChipEnableLow(); + ok &= waitForMisoLow(); + ok &= strobe(SRES); + return ok; + } + + bool Cc1101::initRegister () { + const Register_t *regValues = &PMEM_CC1101_REGISTER_INIT; + bool ok = true; + // DebugPrint_t tmp = debugPrint.print; + // debugPrint.byte = 0xff; // print all + // printRegisters(); + + for (uint8_t addr = 0; ok && addr < sizeof(Register_t); addr++) { + uint8_t regValue; + memcpy_P(®Value, ((const uint8_t *)regValues) + addr, 1); + ok &= writeRegister(addr, regValue); + if ((addr == 0x07) && (regValue & 0x04)) { + status.receivePacketStatusEnabled = true; + } + } + + // debugPrint.print = tmp; + return ok; + } + + bool Cc1101::initPaTable () { + const PATABLE_INIT_ITEM_t *values = PMEM_CC1101_PATABLE_INIT; + uint8_t patable[8]; + uint8_t initValuesIndex = 0; + for (uint8_t i = 0; i < 8; i++) { + memcpy_P(&patable[i], &(values[initValuesIndex++].pa_power), 1); + if (initValuesIndex >= (sizeof(PMEM_CC1101_PATABLE_INIT) / sizeof(PATABLE_INIT_ITEM_t))) { + initValuesIndex = 0; + } + if (debugPrint.print.writePaTable) { + printf_P(PSTR("\nPTABLE %d: 0x%02x"), i, patable[i]); + } + } + return writeRegisters(0x3e, patable, 8); + } + +#endif + + diff --git a/software/deye-sun-12k/src/units/cc1101.hpp b/software/deye-sun-12k/src/units/cc1101.hpp new file mode 100644 index 0000000..3a51ccc --- /dev/null +++ b/software/deye-sun-12k/src/units/cc1101.hpp @@ -0,0 +1,282 @@ +#ifndef CC1101_HPP +#define CC1101_HPP + +#include +#include "../main.hpp" +#include + +class Cc1101 : public TestUnit { + public: + #define PACKET_LENGTH 20 + + public: + typedef enum { Test, Send, Receive } Cc1101Mode; + typedef enum { _IDLE = 0, _RX = 1, _TX = 2, _FSTXON = 3, _CALIBRATE = 4, _SETTLING = 5, _RXFIFO_OVFL = 6, _TXFIFO_UNFL = 7 } StatusState_t; + typedef struct { uint8_t fifoBytes:4; StatusState_t state:3; uint8_t chipNotReady:1; } Status_t; + typedef struct { int8_t rssi:8; uint8_t lqi:7; uint8_t crcOk:1; } ReceivedPacketStatus_t; // CC1101 datasheet page 37 + union { + struct { + union { + Status_t value; + uint8_t byte; + } spiResponse; + union { + ReceivedPacketStatus_t value; + uint8_t byte [2]; + } receivedPacketStatus; + uint8_t spiResponseValid: 1; + uint8_t receivePacketStatusEnabled:1; + uint8_t receivePacketStatusValid:1; + }; + uint32_t dword; + } status; + + public: + Cc1101 (Cc1101Mode mode) { timer = 0; this->mode = mode; status.dword = 0; initDone = false; } + virtual void init (); + virtual void cleanup (); + virtual int8_t run (uint8_t subtest); + virtual PGM_P getName (); + + public: + void tick1ms () { if (timer > 0) timer--; }; + + private: + typedef enum { SRES = 0x30, SCAL = 0x33, SRX = 0x34, STX = 0x35, SFRX = 0x3a, SFTX = 0x3b, SIDLE = 0x36 } StrobeCommand_t; + typedef enum { MARCSTATE = 0x35, TXBYTES = 0x3A, RXBYTES = 0x3B } StatusAddress_t; + typedef struct { uint8_t writeRegister:1; uint8_t writeRegisters:1; uint8_t readRegister:1; uint8_t readRegisters:1; uint8_t writePaTable:1; } DebugPrint_t; + + private: + int8_t runSend (uint8_t subtest); + int8_t runReceive (uint8_t subtest); + int8_t runTest (uint8_t subtest); + uint8_t sendSpiByte (uint8_t b); + void triggerOn(); + void triggerOff(); + void triggerToggle (); + void setChipEnableLow (); + void setChipEnableHigh (); + bool isMisoHigh (); + bool isGd0High (); + bool waitForMisoLow (); + bool waitForGD0High (); + bool waitForGD0Low (); + bool strobe (StrobeCommand_t strobe); + bool writeRegister (uint8_t addr, uint8_t value); + bool writeRegisters (uint8_t addr, uint8_t *buffer, uint8_t length); + bool readRegister (uint8_t addr, uint8_t *value); + bool readRegisters (uint8_t addr, uint8_t *buffer, uint8_t length); + bool readStatusRegister (StatusAddress_t addr, uint8_t *value); + bool sendData (uint8_t *buffer, uint8_t length); + bool receiveData (uint8_t *buffer, uint8_t *receivedLength, uint8_t maxBufferSize); + void printRegisters (); + + bool resetCC1101 (); + bool initRegister (); + bool initPaTable (); + + + private: + bool initDone; + Cc1101Mode mode; + volatile uint8_t timer; + union { + DebugPrint_t print; + uint8_t byte; + } debugPrint; + + + public: + typedef enum { + IOCFG2 = 0x00, IOCFG1 = 0x01, IOCFG0 = 0x02, + FIFOTHR = 0x03, + SYNC1 = 0x04, SYNC0 = 0x05, + PKTLEN = 0x06, + PKTCTRL1 = 0x07, PKTCTRL0 = 0x08, + ADDR = 0x00, CHANNR = 0x0a, + FSCTRL1 = 0x0b, FSCTRL0 = 0x0c, + FREQU2 = 0x0d, FREQU1 = 0x0e, FREQU0 = 0x0f, + MDMCFG4 = 0x10, MDMCFG3 = 0x11, MDMCFG2 = 0x12, MDMCFG1 = 0x13, MDMCFG0 = 0x14, + DEVIATN = 0x15, + MCSM2 = 0x16, MCSM1 = 0x17, MCSM0 = 0x18, + FOCCFG = 0x19, BSCFG = 0x1a, + AGCCTRL2 = 0x1b, AGCCTRL1 = 0x1c, AGCCTRL0 = 0x1d, + WOREVT1 = 0x1e, WOREVT0 = 0x1f, WORCTRL = 0x20, + FREND1 = 0x21, FREND0 = 0x22, + FSCAL3 = 0x23, FSCAL2 = 0x24, FSCAL1 = 0x25, FSCAL0 = 0x26, + RCCTRL1 = 0x27, RCCTRL0 = 0x28, + FSTEST = 0x29, PTEST = 0x2a, AGCTEST = 0x2b, + TEST2 = 0x2c, TEST1 = 0x2d, TEST0 = 0x2e + } RegisterAddress_t; + + typedef enum { + RX_FIFO_FILLED_OR_ABOVE_THRESHHOLD = 0x00, + RX_FIFO_FILLED_OR_ABOVE_THRESHHOLD_OR_END_OF_PACKAGE_UNTIL_FIFO_EMPTY = 0x01, + TX_FIFO_FILLED_OR_ABOVE_THRESHHOLD = 0x02, + TX_FIFO_FULL_UNTIL_BELOW_THRESHHOLD = 0x03, + RX_FIFO_OVERFLOW_UNTIL_FIFO_FLUSHED = 0x04, + TX_FIFO_UNDERFLOW_UNTIL_FIFO_FLUSHED = 0x05, + SYNC_SENT_OR_RECEIVED_UNTIL_END_OF_PACKET = 0x06, + PACKET_RECEIVED_WITH_CRC_OK_UNTIL_FIRST_BYTE_READ_FROM_RXFIFO = 0x07, + PREAMBLE_QUALITY_REACHED_UNTIL_REENTER_RX = 0x08, + RSSI_LEVEL_BELOW_THRESHOLD = 0x09, + LOCK_DETECTOR_OUTPUT = 0x0a, + SERIAL_CLOCK = 0x0b, + SERIAL_SYNCHRONOUS_DATA_OUTPUT = 0x0c, + SERIAL_ASYNC_DATA_OUTPUT = 0x0d, + CARRIER_DETECTED_UNTIL_ENTER_IDLE = 0x0e, + CRC_OK_UNTIL_REENTER_RX = 0x0f, + RX_HARD_DATA_1 = 0x16, + RX_HARD_DATA_0 = 0x17, + PA_PD = 0x1b, + LNA_PD = 0x1c, + RX_SYMBOL_TICK = 0x1d, + WAKEUP_ON_RECEIVE_EVENT0 = 0x24, + WAKEUP_ON_RECEIVE_EVENT1 = 0x25, + CLK_256 = 0x26, + CLK_32K = 0x27, + CHIP_NOT_READY = 0x29, + XOSC_STABLE = 0x2b, + HIGH_IMPEDANCE_WHEN_CHIP_SELECT_HIGH = 0x2e, + HW_TO_0 = 0x2f, + CLK_XOSC = 0x30, + CLK_XOSC_DIV_1P5 = 0x31, + CLK_XOSC_DIV_2 = 0x32, + CLK_XOSC_DIV_3 = 0x33, + CLK_XOSC_DIV_4 = 0x34, + CLK_XOSC_DIV_6 = 0x35, + CLK_XOSC_DIV_8 = 0x36, + CLK_XOSC_DIV_12 = 0x37, + CLK_XOSC_DIV_16 = 0x38, + CLK_XOSC_DIV_24 = 0x39, + CLK_XOSC_DIV_32 = 0x3a, + CLK_XOSC_DIV_48 = 0x3b, + CLK_XOSC_DIV_64 = 0x3c, + CLK_XOSC_DIV_96 = 0x3d, + CLK_XOSC_DIV_128 = 0x3e, + CLK_XOSC_DIV_192 = 0x3f + } GDOx_CFG_t; + + typedef enum { + SLEEP = 0, + IDLE = 1, + XOFF = 2, + MANCAL_VCOON = 3, + MANCAL_REGON = 4, + MANCAL = 5, + FS_WAKEUP_VCOON = 6, + FS_WAKEUP_REGON = 7, + CALIBRATE_START = 8, + SETTLING_BWBOOST = 9, + SETTLING_FS_LOCK = 10, + SETTLIN_IFADCON = 11, + CALIBRATE_END = 12, + RX = 13, + RX_END = 14, + RX_RST = 15, + TXRX_SETTLING = 16, + RXFIFO_OVERFLOW = 17, + FXTXON = 18, + TX = 19, + TX_END = 20, + RXTX_SETTLING = 21, + TXFIFO_UNDERFLOW = 22, + UNKNOWN = 255 + } MainRadioControlState; + + typedef struct { uint8_t pa_power; const char dbm[4]; } PATABLE_INIT_ITEM_t; + + typedef enum { NO_ADDR_CHECK = 0, CHECK_NO_BROADCAST = 1, CHECK_WITH_BROADCAST_0 = 2, CHECK_WITH_BROADCAST_0_AND_255 = 3 } ADR_CHK_t; + typedef enum { FIXED = 0, VARIABLE = 1, INFINITE = 2 } LENGTH_CONFIG_t; + typedef enum { NORMAL_USE_FIFO = 0, SYNC_SERIAL = 1, RANDOM_TX = 2, ASYNC_SERIAL = 3 } PKT_FORMAT_t; + typedef enum { FSK2 = 0, GFSK = 1, ASK_OOK = 3, FSK4 = 4, MSK = 7 } MOD_FORMAT_t; + typedef enum { NO_SYNC = 0, SYNC_15_16 = 1, SYNC_16_16 = 2, SYNC_30_32 = 3, CARRIER_SENSE = 4, SYNC_15_16_CARRIER_SENSE = 5, SYNC_16_16_CARRIER_SENSE = 6 , SYNC_30_326_CARRIER_SENSE = 7} SYNC_MODE_t; + typedef enum { TWO = 0, THREE = 1, FOUR = 2, SIX = 3, EIGHT = 4, TWELVE = 5, SIXTEEN = 6, TWENTYFOUR = 7 } NUM_PREAMBLE_t; + typedef enum { TXOFF_IDLE = 0, TXOFF_FSTXON = 1, STAY_IN_TX = 2, TXOFF_RX = 3 } TXOFF_MODE_t; + typedef enum { RXOFF_IDLE = 0, RXOFF_FSTXON = 1, RXOFF_TX = 2, STAY_IN_RX = 3 } RXOFF_MODE_t; + typedef enum { ALWAYS = 0, RSSI_BELOW_THRESHOLD = 1, UNLESS_RECEIVE_PACKET = 2, RSSI_BELOW_THRESHOLD__UNLESS_RECEIVE_PACKET = 3 } CCA_MODE_t; + typedef enum { NEVER = 0, IDLE_TO_RX_OR_TX = 1, RX_OR_TX_TO_IDLE = 2, RX_OR_TX_TO_IDLE_EVERY_4_TIME = 3 } FS_AUTOCAL_t; + + typedef struct { GDOx_CFG_t gdo0_cfg:6; uint8_t gdo0_inv:1; uint8_t bit7:1; } IOCFG0_t; + typedef struct { GDOx_CFG_t gdo1_cfg:6; uint8_t gdo1_inv:1; uint8_t bit7:1; } IOCFG1_t; + typedef struct { GDOx_CFG_t gdo2_cfg:6; uint8_t gdo2_inv:1; uint8_t bit7:1; } IOCFG2_t; + typedef struct { uint8_t fifo_thr:4; uint8_t close_in_rx:2; uint8_t adc_retention:1; uint8_t bit7:1; } FIFOTHR_t; + typedef struct { ADR_CHK_t adr_chk:2; uint8_t append_status:1; uint8_t crc_autoflush:1; uint8_t bit4:1; uint8_t pqt:3; } PKTCTRL1_t; + typedef struct { LENGTH_CONFIG_t length_config:2; uint8_t crc_en:1; uint8_t bit3:1; PKT_FORMAT_t pkt_format:2; uint8_t white_data:1; uint8_t bit7:1; } PKTCTRL0_t; + typedef struct { uint8_t frequ_if:5; uint8_t bit5:1; uint8_t bit76:2; } FSCTRL1_t; + typedef struct { uint8_t frequoff:8; } FSCTRL0_t; + typedef struct { uint8_t drate_e:4; uint8_t chanbw_m:2; uint8_t chanbw_e:2; } MDMCFG4_t; + typedef struct { uint8_t drate_m:8; } MDMCFG3_t; + typedef struct { SYNC_MODE_t sync_mode:3; uint8_t manchester_en:1; MOD_FORMAT_t mod_format:3; uint8_t dem_dcfilt_off:1; } MDMCFG2_t; + typedef struct { uint8_t chanspc_e:2; uint8_t bit32:2; NUM_PREAMBLE_t num_preamble:3; uint8_t fec_en:1; } MDMCFG1_t; + typedef struct { uint8_t chanspc_m:8; } MDMCFG0_t; + typedef struct { uint8_t deviation_m:3; uint8_t bit3:1; uint8_t deviation_e:3; uint8_t bit7:1; } DEVIATN_t; + typedef struct { uint8_t rx_time:3; uint8_t rx_time_qual:1; uint8_t rx_time_rssi:1; uint8_t bit765:3; } MCSM2_t; + typedef struct { TXOFF_MODE_t txoff_mode:2; RXOFF_MODE_t rxoff_mode:2; CCA_MODE_t cca_mode:2; uint8_t bit76:2; } MCSM1_t; + typedef struct { uint8_t xosc_force_on:1; uint8_t pin_ctrl_en:1; uint8_t po_timeout:2; FS_AUTOCAL_t fs_autocal:2; uint8_t bit76:2; } MCSM0_t; + typedef struct { uint8_t pa_power:3; uint8_t bit3:1; uint8_t lodiv_buf_current:2; uint8_t bit76:2; } FREND0_t; + + typedef union { + uint8_t byte; + struct { + uint8_t num_rxbytes:7; + uint8_t rxfifo_overflow:1; + } rxbytes; + } STATUS_RXBYTES_t; + + typedef struct { + IOCFG2_t iocfg2; + IOCFG1_t iocfg1; + IOCFG0_t iocfg0; + FIFOTHR_t fifothr; + uint8_t sync1; + uint8_t sync0; + uint8_t pktlen; + PKTCTRL1_t pktctrl1; + PKTCTRL0_t pktctrl0; + uint8_t addr; + uint8_t channr; + FSCTRL1_t fsctrl1; + FSCTRL0_t fsctrl0; + uint8_t frequ2; + uint8_t frequ1; + uint8_t frequ0; + MDMCFG4_t mdmcfg4; + MDMCFG3_t mdmcfg3; + MDMCFG2_t mdmcfg2; + MDMCFG1_t mdmcfg1; + MDMCFG0_t mdmcfg0; + DEVIATN_t deviatn; + MCSM2_t mcsm2; + MCSM1_t mcsm1; + MCSM0_t mcsm0; + uint8_t foccfg; + uint8_t bscfg; + uint8_t agcctrl2; + uint8_t agcctrl1; + uint8_t agcctrl0; + uint8_t worevt1; + uint8_t worevt0; + uint8_t worctrl; + uint8_t frend1; + FREND0_t frend0; + uint8_t fscal3; + uint8_t fscal2; + uint8_t fscal1; + uint8_t fscal0; + uint8_t rcctrl1; + uint8_t rcctrl0; + uint8_t fstest; + uint8_t ptest; + uint8_t agctest; + uint8_t test2; + uint8_t test1; + uint8_t test0; + + } Register_t; + + + +}; + +#endif \ No newline at end of file diff --git a/software/deye-sun-12k/src/units/deye.cpp b/software/deye-sun-12k/src/units/deye.cpp new file mode 100644 index 0000000..71cd961 --- /dev/null +++ b/software/deye-sun-12k/src/units/deye.cpp @@ -0,0 +1,231 @@ +#include +#include +#include + +#include "deye.hpp" +#include "../main.hpp" + + +#ifdef __AVR_ATmega328P__ +void Modbus::init () {} +void Modbus::cleanup () {} +int8_t Modbus::run (uint8_t subtest) { return -1; } +void Modbus::handleRxByte (uint8_t b) {} +#endif + +#if defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__) + +// PB0 ... nRE .. Read enable +// PB1 ... DE .. Data enable + +#define SET_nRE (PORTB |= (1 << PB0)) +#define CLR_nRE (PORTB &= ~(1 << PB0)) +#define SET_DE (PORTB |= (1 << PB1)) +#define CLR_DE (PORTB &= ~(1 << PB1)) + + +void Deye::init () { +} + +void Deye::cleanup () { + enabled = 0; + UCSR1A = 0; + UCSR1B = 0; + UCSR1C = 0; + UBRR1H = 0; + UBRR1L = 0; + PORTD &= ~(1 << PD2); + DDRB &= ~((1 << PB1) | (1 << PB0)); + PORTB &= ~((1 << PB1) | (1 << PB0)); +} + +uint16_t Deye::updateModbusCRC (uint8_t b, uint16_t crc) { + crc = crc ^ (uint16_t)b; + for (uint8_t i = 0; i < 8; i++) { + crc = crc & 0x01 ? (crc >> 1) ^ 0xa001 : crc >> 1; + } + return crc; +} + + +int8_t Deye::run (uint8_t subtest) { + if (subtest == 0) { + SET_nRE; + CLR_DE; + DDRB |= (1 << PB1) | (1 << PB0); + + // UART1 interface on Nano-644 + PORTD |= (1 << PD2); // enable RxD1 pullup + UCSR1A = (1 << U2X1); + UCSR1B = (1 << RXCIE1) | (1 << RXEN1) | (1 <> 8); frame[3] = (uint8_t)(addr & 0xff); + frame[4] = (uint8_t)(quantity >> 8); frame[5] = (uint8_t)(quantity & 0xff); + uint16_t crc = updateModbusCRC(frame[0], 0xffff); + for (uint8_t i = 1; i < 6; i++) { + crc = updateModbusCRC(frame[i], crc); + } + frame[6] = (uint8_t)(crc & 0xff); + frame[7] = (uint8_t)(crc >> 8); + + SET_DE; CLR_nRE; _delay_us(500); receivedBytes = 0; + for (uint8_t i = 0; i < 8; i++) { + UCSR1A |= (1 << TXC1); + UDR1 = frame[i]; + while ((UCSR1A & (1 <> 8); + frame[3] = (uint8_t)(addr & 0xff); + uint16_t crc = updateModbusCRC(frame[0], 0xffff); + for (uint8_t i = 1; i < sizeof(frame) - 2; i++) { + crc = updateModbusCRC(frame[i], crc); + } + frame[sizeof(frame) - 2] = (uint8_t)(crc & 0xff); + frame[sizeof(frame) - 1] = (uint8_t)(crc >> 8); + + SET_DE; + CLR_nRE; + + for (uint32_t addr = 0; addr < 65535; addr++) { + + /// uint16_t addr = 1; + // printf_P(PSTR("Modbus: lese Register %d (0x%04x)"), addr, addr); + + frame[2] = (uint8_t)( (addr >> 8) & 0xff); + frame[3] = (uint8_t)(addr & 0xff); + uint16_t crc = updateModbusCRC(frame[0], 0xffff); + for (uint8_t i = 1; i < sizeof(frame) - 2; i++) { + crc = updateModbusCRC(frame[i], crc); + } + frame[sizeof(frame) - 2] = (uint8_t)(crc & 0xff); + frame[sizeof(frame) - 1] = (uint8_t)(crc >> 8); + + SET_DE; + _delay_us(500); + receivedBytes = 0; + for (uint8_t i = 0; i < sizeof(frame); i++) { + UCSR1A |= (1 << TXC1); + UDR1 = frame[i]; + while ((UCSR1A & (1 < Sending:")); + // for (uint8_t i = 0; i < sizeof(frame); i++) { + // printf_P(PSTR(" 0x%02x"), frame[i]); + // } + int k = wait(100); + + if (receivedBytes != 15 || received[8] != 1 || received[9] != 3 || received[10] != 2) { + printf_P(PSTR("%u ERROR => Sending"), (uint16_t)addr ); + for (uint8_t i = 0; i < sizeof(frame); i++) { + printf_P(PSTR(" 0x%02x"), frame[i]); + } + printf_P(PSTR(" => ")); + if (receivedBytes == 0) { + printf_P(PSTR("?")); + } else { + for (uint8_t i = 0; i < receivedBytes; i++) { + if (i == sizeof(frame)) { + printf_P(PSTR(" ")); + } + printf_P(PSTR(" 0x%02x"), received[i]); + } + } + printf_P(PSTR("\n")); + } else { + printf_P(PSTR("%u %u\n"), (uint16_t)addr, received[11] << 8 | received[12] ); + } + // printf_P(PSTR("\n RxD1:")); + // if (receivedBytes == 0) { + // printf_P(PSTR("?")); + // } else { + // for (uint8_t i = 0; i < receivedBytes; i++) { + // if (i == sizeof(frame)) { + // printf_P(PSTR(" ")); + // } + // printf_P(PSTR(" 0x%02x"), received[i]); + // } + // } + // if (receivedBytes >= 16) { + // union { + // uint8_t b[4]; + // float value; + // } f; + // f.b[0] = received[14]; + // f.b[1] = received[13]; + // f.b[2] = received[12]; + // f.b[3] = received[11]; + // printf_P(PSTR(" -> %4.8fV\n"), (double)f.value); + // } + if (k != EOF) { + break; + } + + } while (wait(1000) == EOF); + + } else { + printf_P(PSTR("end")); + return -1; + } + wait(500); + return 0; +} + +void Deye::handleRxByte (uint8_t b) { + if (receivedBytes < sizeof(received)) { + received[receivedBytes++] = b; + } +} + +#endif diff --git a/software/deye-sun-12k/src/units/deye.hpp b/software/deye-sun-12k/src/units/deye.hpp new file mode 100644 index 0000000..a79c6da --- /dev/null +++ b/software/deye-sun-12k/src/units/deye.hpp @@ -0,0 +1,25 @@ +#ifndef DEYE_HPP +#define DEYE_HPP + +#include +#include "../main.hpp" +#include + +class Deye : public TestUnit { + public: + uint8_t enabled; + uint8_t receivedBytes; + uint8_t received[128]; + + + public: + Deye () { enabled = 0; receivedBytes = 0; } + virtual void init (); + virtual void cleanup (); + virtual int8_t run (uint8_t subtest); + virtual PGM_P getName () { return PSTR("Deye"); } + uint16_t updateModbusCRC (uint8_t b, uint16_t crc); + void handleRxByte (uint8_t); +}; + +#endif \ No newline at end of file diff --git a/software/deye-sun-12k/src/units/encoder.cpp b/software/deye-sun-12k/src/units/encoder.cpp new file mode 100644 index 0000000..7b33b76 --- /dev/null +++ b/software/deye-sun-12k/src/units/encoder.cpp @@ -0,0 +1,138 @@ +#include +#include + +#include "encoder.hpp" +#include "../main.hpp" + +// Encoder signals on rotation clockwise 1 step: +// A -----____------ one char app. 1ms..2ms (rotation speed) +// B -------___----- +// one step when: A = 0, B= 1->0 + +// Encoder signals on rotation counterclockwise 1 step: +// A -----____------ one char app. 1ms..2ms (rotation speed) +// B --______----- +// one step when: A = 0, B= 0->1 + +#if defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__) + + // Nano-644 + // --------------------------------------------------------------- + // PB0/T0 ... Encoder A + // PB1/T1 ... Encoder B + // PB2/INT2 ... push switch of encoder (pushed = 0) + + void Encoder::init () { + DDRB &= ~((1 << PB2) | (1 << PB1) | (1 << PB0)); + PORTB |= (1 << PORTB2) | (1 << PORTB1) | (1 << PORTB0); // enable pullup + enabled = 1; + } + + void Encoder::cleanup () { + enabled = 0; + DDRB &= ~((1 << PB2) | (1 << PB1) | (1 << PB0)); + PORTB &= ~((1 << PORTB2) | (1 << PORTB1) | (1 << PORTB0)); + } + + bool Encoder::isPressed () { + return (PINB & (1 << PB2)) == 0; + } + + bool Encoder::getA () { + return (PINB & (1 << PB0)) == 0; + } + + bool Encoder::getB () { + return (PINB & (1 << PB1)) == 0; + } + + +#endif + +#ifdef __AVR_ATmega328P__ + + // Arduino-Nano-5V + // --------------------------------------------------------------- + // PD4/T0 ... Encoder A + // PB0 ... Encoder B + // PD7 ... push switch of encoder (pushed = 0) + + void Encoder::init () { + DDRB &= ~(1 << PB0); + DDRD &= ~((1 << PD7) | (1 << PD4)); + PORTB |= (1 << PB0); // enable pullup + PORTD |= (1 << PD7) | (1 << PD4); // enable pullup + enabled = 1; + } + + void Encoder::cleanup () { + enabled = 0; + PORTB &= ~(1 << PB0); + PORTD &= ~((1 << PD7) | (1 << PD4)); + DDRB &= ~(1 << PB0); + DDRD &= ~((1 << PD7) | (1 << PD4)); + } + + bool Encoder::isPressed () { + return (PIND & (1 << PD7)) == 0; + } + + bool Encoder::getA () { + return (PIND & (1 << PD4)) == 0; + } + + bool Encoder::getB () { + return (PINB & (1 << PB0)) == 0; + } + +#endif + + +int8_t Encoder::run (uint8_t subtest) { + switch (subtest) { + case 0: { + while (wait(10) == EOF) { + printf_P(PSTR("\r => Encoder (push to clear): ")); + printf_P(PSTR("%5d (0x%02x) "), count, (uint8_t)count); + if (isPressed()) { + reset(); + } + } + return 0; + } + } + + return -1; +} + +struct EncoderState { + int8_t a:1; // signal A + int8_t b:1; // signal B +}; + +void Encoder::tick100us () { + static EncoderState lastState = { 1, 1 }; + static EncoderState lastStableState = { 1, 1 }; + + if (!enabled) { + count = 0; + return; + } + EncoderState nextState; + nextState.a = getA() ? 1 : 0; + nextState.b = getB() ? 1 : 0; + if (nextState.a == lastState.a && nextState.b == lastState.b) { + if (lastStableState.a == 0 && nextState.b != lastStableState.b) { + if (nextState.b == 0) { + count = count < 127 ? count + 1 : 127; + } else { + count = count > -128 ? count - 1 : -128; + } + } + lastStableState.a = nextState.a; + lastStableState.b = nextState.b; + } + lastState.a = nextState.a; + lastState.b = nextState.b; +} + diff --git a/software/deye-sun-12k/src/units/encoder.hpp b/software/deye-sun-12k/src/units/encoder.hpp new file mode 100644 index 0000000..9b0861b --- /dev/null +++ b/software/deye-sun-12k/src/units/encoder.hpp @@ -0,0 +1,28 @@ +#ifndef ENCODER_HPP +#define ENCODER_PP + +#include +#include "../main.hpp" +#include + +class Encoder : public TestUnit { + public: + uint8_t enabled; + int8_t count; + + public: + Encoder () { reset(); enabled = 0; }; + virtual void init (); + virtual void cleanup (); + virtual int8_t run (uint8_t subtest); + virtual PGM_P getName () { return PSTR("Encoder"); } + void reset () { count = 0; } + void tick100us (); + bool isPressed (); + + private: + bool getA (); + bool getB (); +}; + +#endif \ No newline at end of file diff --git a/software/deye-sun-12k/src/units/i2c.cpp b/software/deye-sun-12k/src/units/i2c.cpp new file mode 100644 index 0000000..60dd22d --- /dev/null +++ b/software/deye-sun-12k/src/units/i2c.cpp @@ -0,0 +1,216 @@ +#include +#include +#include +#include +#include + +#include "i2c.hpp" +#include "../adafruit/bme280.h" +#include "../main.hpp" + +// Sparkfun https://www.sparkfun.com/products/22858 +// ENS160 address 0x53 (air quality sensor) +// BME280 address 0x77 /humidity/temperature sensor) +// register 0xfe: humidity_7:0 +// register 0xfd: humidity_15:8 + +PGM_P I2c::getName () { + switch (mode) { + case SparkFunEnvCombo: return PSTR("I2C-Sparkfun Env-Combo"); + case Master: return PSTR("I2C-Master"); + case Slave: return PSTR("I2C-Slave"); + } + return "?"; +} + +void I2c::init () { + TWBR = 13; // 100kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 100000) / (2 * 100000 * 4); + TWBR = 28; // 50kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 50000) / (2 * 50000 * 4); + TWBR = 100; // 50kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 50000) / (2 * 50000 * 4); + TWCR = (1 << TWEN); + ADMUX = (1 << ADLAR) | (1 << REFS0); // ADC0, VREF=AVCC=3.3V + ADCSRA = (1 << ADEN) | 7; // ADC Enable, Prescaler 128 + enabled = true; +} + +void I2c::cleanup () { + enabled = false; + TWCR = (1 << TWEN); + TWBR = 0; + ADMUX = 0; + ADCSRA = 0; +} + +int8_t I2c::run (uint8_t subtest) { + if (subtest == 0 && mode == I2c::SparkFunEnvCombo) { + printf_P(PSTR(" BM280 ... ")); + if (!bm280.begin()) { + printf_P(PSTR("E1")); + return -1; + } + printf_P(PSTR("OK, ENS160 ... ")); + if (!ens160.begin()) { + printf_P(PSTR("E2")); + return -1; + } + if (!ens160.setMode(ENS160_OPMODE_STD)) { + printf_P(PSTR("E3")); + return -1; + } + if (!ens160.set_envdata(25.0, 65)) { + printf_P(PSTR("E4")); + return -1; + } + + printf_P(PSTR("OK")); + float accTemp = 0, accHumidity = 0; + int8_t accCount = -1; + + do { + // BME280 + float p = bm280.readPressure(); + printf_P(PSTR("\n => BM280: P= %.3fbar"), (double)p / 100000.0); + float t = bm280.readTemperature(); + printf_P(PSTR(", T= %.2f°C"), (double)t); + float h = bm280.readHumidity(); + printf_P(PSTR(", H= %.2f%%"), (double)h); + + if (accCount >= 0 && !isnanf(h) && !isnan(t)) { + accTemp += t; + accHumidity += h; + accCount++; + } + + bm280.setSampling( + Adafruit_BME280::MODE_NORMAL, + Adafruit_BME280::SAMPLING_X16, + Adafruit_BME280::SAMPLING_X16, + Adafruit_BME280::SAMPLING_X16, + Adafruit_BME280::FILTER_OFF, + Adafruit_BME280::STANDBY_MS_1000 + ); + + // ENS160 only activated every 32s to avoid wrong temperature measuerment + // if ES160 would be continously active, the temperature would be 4°C higher + // -> ES160 has not enough distance to BM280 + // This solution causes only a +0.3°C higher temperatur value + if (accCount < 0 || accCount >= 32) { + printf_P(PSTR(" | ENS160 (")); + if (accCount > 0) { + h = accHumidity / accCount; + t = accTemp / accCount; + accTemp = 0; + accHumidity = 0; + } + accCount = 0; + if (!ens160.set_envdata(t, h)) { + printf_P(PSTR("E1)")); + } else { + printf_P(PSTR("%.1f°C/%.1f%%): "), (double)t, (double)h); + if (!ens160.setMode(ENS160_OPMODE_STD)) { + printf_P(PSTR("E2")); + } else { + for (uint8_t i = 0; i < 100; i++) { + _delay_ms(15); + uint8_t status; + if (ens160.readStatus(&status)) { + if (status & ENS160_DATA_STATUS_NEWDAT) { + ENS160_DATA data; + if (ens160.readData(&data)) { + printf_P(PSTR(" aqi=%d("), data.aqi); + switch(data.aqi) { + case 1: printf_P(PSTR("excellent")); break; + case 2: printf_P(PSTR("good")); break; + case 3: printf_P(PSTR("moderate")); break; + case 4: printf_P(PSTR("poor")); break; + case 5: printf_P(PSTR("unhealthy")); break; + default: printf_P(PSTR("?")); break; + } + printf_P(PSTR("), tvoc=%dppb"), data.tvoc); + printf_P(PSTR(", eco2=%d("), data.eco2); + if (data.eco2 < 400) { + printf_P(PSTR("?")); + } else if (data.eco2 < 600) { + printf_P(PSTR("excellent")); + } else if (data.eco2 < 800) { + printf_P(PSTR("good")); + } else if (data.eco2 < 1000) { + printf_P(PSTR("fair")); + } else if (data.eco2 < 1500) { + printf_P(PSTR("poor")); + } else { + printf_P(PSTR("bad")); + } + printf_P(PSTR(")")); + } + break; + } + } + } + } + if (!ens160.setMode(ENS160_OPMODE_IDLE)) { + printf_P(PSTR("E3")); + } + } + } + + } while (wait(1000) == EOF); + + } else if (subtest == 0 && mode == I2c::Master) { + if (!master.begin(0x01)) { + printf_P(PSTR("E1")); + return -1; + } + do { + uint8_t buffer[1]; + // read poti + ADCSRA |= (1 << ADSC); // start ADC + while (ADCSRA & (1 << ADSC)) {} // wait for result + buffer[0] = ADCH; + printf_P(PSTR("\n I2C-MASTER: to slave: 0x%02x"), buffer[0]); + if (!master.write(buffer, 1)) { + printf_P(PSTR(" -> ERROR")); + } + printf_P(PSTR(", from slave: ")); + if (master.read(buffer, 1)) { + printf_P(PSTR("0x%02x"), buffer[0]); + } else { + printf_P(PSTR(" -> ERROR")); + } + } while (wait(1000) == EOF); + master.end(); + + } else if (subtest == 0 && mode == I2c::Slave) { + if (!slave.begin(0x01, false)) { + printf_P(PSTR("E1")); + return -1; + } + do { + int fromMaster = slave.read(); + if (fromMaster != EOF) { + ADCSRA |= (1 << ADSC); // start ADC + while (ADCSRA & (1 << ADSC)) {} // wait for result + slave.write(ADCH); + printf_P(PSTR("\n I2C SLAVE: from master: 0x%02x -> to master: 0x%02x"), fromMaster, ADCH); + } + } while (wait(0) == EOF); + slave.end(); + + } else { + printf_P(PSTR("end")); + return -1; + } + wait(500); + return 0; +} + +void I2c::handleTwiIrq () { + if (mode == I2c::Slave) { + DDRD |= (1 << PD7); + PORTD |= (1 << PD7); + slave.handleTWIIsr(); + PORTD &= ~(1 << PD7); + } else { + TWCR |= (1 << TWINT); // clear Interrupt Request + } +} diff --git a/software/deye-sun-12k/src/units/i2c.hpp b/software/deye-sun-12k/src/units/i2c.hpp new file mode 100644 index 0000000..2148cc0 --- /dev/null +++ b/software/deye-sun-12k/src/units/i2c.hpp @@ -0,0 +1,43 @@ +#ifndef I2C_HPP +#define I2C_HPP + +#include +#include "../main.hpp" +#include "../adafruit/bme280.h" +#include "../adafruit/ens160.h" +#include "../i2cmaster.hpp" +#include "../i2cslave.hpp" + + +class I2c : public TestUnit { + public: + typedef enum I2cMode { SparkFunEnvCombo, Master, Slave } I2cMode; + + private: + I2cMode mode; + Adafruit_BME280 bm280; + ScioSense_ENS160 ens160; + I2cMaster master; + I2cSlave slave; + + public: + bool enabled; + + public: + I2c (I2cMode mode) { enabled = false; this->mode = mode; } + void tick1ms () { master.tick1ms(); slave.tick1ms(); } + virtual void init (); + virtual void cleanup (); + virtual int8_t run (uint8_t subtest); + virtual PGM_P getName (); + void handleTwiIrq (); + // uint16_t startRead (uint8_t address); + // uint16_t startWrite (uint8_t address); + // void stop (); + // uint16_t writeByte (uint8_t data); + // uint16_t writeData (uint8_t size, const uint8_t *data); + // uint16_t readData (uint8_t size, uint8_t *data); + // int32_t compensateBm280T (int32_t adcT); +}; + +#endif \ No newline at end of file diff --git a/software/deye-sun-12k/src/units/ieee485.cpp b/software/deye-sun-12k/src/units/ieee485.cpp new file mode 100644 index 0000000..8fcb67a --- /dev/null +++ b/software/deye-sun-12k/src/units/ieee485.cpp @@ -0,0 +1,111 @@ +#include +#include +#include +#include + +#include "ieee485.hpp" +#include "../main.hpp" + +#ifdef __AVR_ATmega328P__ + +// Nano-328P +// ------------------------------------ +// IEE485 not supported (no UART1) + +void Ieee485::init () {} +void Ieee485::cleanup () {} +int8_t Ieee485::run (uint8_t subtest) { + return -1; +} + +#endif + + +#if defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__) + +// Nano-644 +// ------------------------------------ +// PB0 ... nRE .. Read enable +// PB1 ... DE .. Data enable + +#define SET_nRE (PORTB |= (1 << PB0)) +#define CLR_nRE (PORTB &= ~(1 << PB0)) +#define SET_DE (PORTB |= (1 << PB1)) +#define CLR_DE (PORTB &= ~(1 << PB1)) + +void Ieee485::init () { + // Poti + ADMUX = (1 << ADLAR) | (1 << REFS0); // ADC0, VREF=AVCC=3.3V + ADCSRA = (1 << ADEN) | 7; // ADC Enable, Prescaler 128 + + // Modbus + SET_nRE; + CLR_DE; + DDRB |= (1 << PB1) | (1 << PB0); + + // UART1 interface on Nano-644 + PORTD |= (1 << PD2); // enable RxD1 pullup + UCSR1A = (1 << U2X1); + UCSR1B = (1 << RXCIE1) | (1 << RXEN1) | (1 < send Byte 0x%02x"), ADCH); + int b; + ATOMIC_BLOCK(ATOMIC_FORCEON) { + b = receivedByte; + receivedByte = -1; + } + if (b >= 0) { + printf_P(PSTR("\n => receive Byte: 0x%02x"), b); + } + } + + } else { + printf_P(PSTR("end")); + return -1; + } + + return 0; +} + +void Ieee485::handleRxByte (uint8_t b) { + receivedByte = b; +} + +#endif \ No newline at end of file diff --git a/software/deye-sun-12k/src/units/ieee485.hpp b/software/deye-sun-12k/src/units/ieee485.hpp new file mode 100644 index 0000000..ffbb15c --- /dev/null +++ b/software/deye-sun-12k/src/units/ieee485.hpp @@ -0,0 +1,22 @@ +#ifndef IEEE485_HPP +#define IEEE485_HPP + +#include +#include "../main.hpp" +#include + +class Ieee485 : public TestUnit { + public: + uint8_t enabled; + int16_t receivedByte; + + public: + Ieee485 () { enabled = 0; receivedByte = -1; } + virtual void init (); + virtual void cleanup (); + virtual int8_t run (uint8_t subtest); + virtual const char *getName () { return PSTR("IEEE485"); } + void handleRxByte (uint8_t); +}; + +#endif \ No newline at end of file diff --git a/software/deye-sun-12k/src/units/lcd.cpp b/software/deye-sun-12k/src/units/lcd.cpp new file mode 100644 index 0000000..e779c16 --- /dev/null +++ b/software/deye-sun-12k/src/units/lcd.cpp @@ -0,0 +1,443 @@ +#include +#include +#include + +#include "lcd.hpp" +#include "../main.hpp" + +#if defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__) + + // Nano-644 / Nano-1284 + // --------------------------------------------------------------- + + // Nano-X-Base V1a V2a Beschreibung + // --------------------------------- ------- + // BL ........... PC0 PC0 Backlight ON(=1) / OFF(=0) + // E ........... PA3 PA3 LCD Enable (Verbindung via J25 erforderlich) + // R/W .......... PD6 PD6 Read/Write: Read=1, Write=0 + // RS ........... PD7 PD7 Register Select: Command=0, Data=1 + // Data ......... PB7:0 PB7:0 Achtung von 5V LCD nicht lesen! + + void Lcd::init () { + DDRA |= (1 << PA3) | (1 << PA0); + DDRB = 0xff; + DDRD |= (1 << PD7) | (1 << PD6); + initLcd(); + printf_P(PSTR("init LCD (")); + if (status == 1) { + printf_P(PSTR("OK)")); + } else { + printf_P(PSTR("ERROR %d)"), status); + } + setBacklightOn(); + } + + void Lcd::cleanup () { + clear(); + PORTA &= ~((1 << PA3) | (1 << PA0)); + DDRA &= ~((1 << PA3) | (1 << PA0)); + PORTB = 0; + DDRB = 0; + PORTD &= ~((1 << PD7) | (1 << PD6)); + DDRD &= ~((1 << PD7) | (1 << PD6)); + } + + void Lcd::setRS () { PORTD |= (1 << PD7); } + void Lcd::clrRS () { PORTD &= ~(1 << PD7); } + void Lcd::setRW () { PORTD |= (1 << PD6); } + void Lcd::clrRW () { PORTD &= ~(1 << PD6); } + void Lcd::setE () { PORTA |= (1 << PA3); } + void Lcd::clrE () { PORTA &= ~(1 << PA3); } + void Lcd::dataDirectionOut () { DDRB = 0xff; } + + void Lcd::dataDirectionIn () { + if (hardwareVersion == 1) { + // read back not allowed (missing level shifter 5V -> 3.3V) + DDRB = 0xff; + } else { + DDRB = 0x00; + } + } + + uint8_t Lcd::getData () { + if (hardwareVersion == 1) { + // read back not allowed (missing level shifter 5V -> 3.3V) + _delay_ms(1); + return 0x00; // bit 8 (busy) = 0 + } else { + return PINB; + } + } + + void Lcd::setData (uint8_t data) { + PORTB = data; + } + + void Lcd::setBacklightOn () { + PORTA |= (1 << PA0); + } + + void Lcd::setBacklightOff () { + PORTA &= ~(1 << PA0); + } + +#endif + +#ifdef __AVR_ATmega328P__ + + // Arduino Nano (5V) + // --------------------------------------------------------------- + + // Nano-X-Base V1a V2a Beschreibung + // --------------------------------- ------- + // BL ........... PC0 PC0 Backlight ON(=1) / OFF(=0) + // E ........... PC3 PC3 LCD Enable (Verbindung via J25 erforderlich) + // R/W .......... PD3 PD3 Read/Write: Read=1, Write=0 + // RS ........... PD2 PD2 Register Select: Command=0, Data=1 + // Data0 ........ PD4 PD4 + // Data1 .........PB0 PB0 + // Data2 .........PD7 PD7 + // Data3 .........PD6 PD6 + // Data4 .........PB2 PB2 + // Data5 .........PB3 PB3 + // Data6 .........PB4 PB4 + // Data7 .........PB5 PB5 + + // PC3 ..... E --> LCD Enable (Verbindung via J25 erforderlich) + // PD3 ..... R/W --> Read/Write: Read=1, Write=0 + // PD2 ..... RS --> Register Select: Command=0, Data=1 + + + void Lcd::init () { + clrRW(); + clrRS(); + clrE(); + setData(0); + DDRB |= (1 << PB5) | (1 << PB4) | (1 << PB3) | (1 << PB2) | (1 << PB0); + DDRC |= (1 << PC3) | (1 << PC0); + DDRD |= (1 << PD7) | (1 << PD6) | (1 << PD4) | (1 << PD3) | (1 << PD2); + initLcd(); + printf_P(PSTR("init LCD (")); + if (status == 1) { + printf_P(PSTR("OK)")); + } else { + printf_P(PSTR("ERROR %d)"), status); + } + setBacklightOn(); + } + + void Lcd::cleanup () { + clear(); + PORTB &= ~((1 << PB5) | (1 << PB4) | (1 << PB3) | (1 << PB2) | (1 << PB0)); + PORTC &= ~((1 << PC3) | (1 << PC0)); + PORTD &= ~((1 << PD7) | (1 << PD6) | (1 << PD4) | (1 << PD3) | (1 << PD2)); + DDRB &= ~((1 << PB5) | (1 << PB4) | (1 << PB3) | (1 << PB2) | (1 << PB0)); + DDRC &= ~((1 << PC3) | (1 << PC0)); + DDRD &= ~((1 << PD7) | (1 << PD6) | (1 << PD4) | (1 << PD3) | (1 << PD2)); + } + + void Lcd::setRS () { PORTD |= (1 << PD2); } + void Lcd::clrRS () { PORTD &= ~(1 << PD2); } + void Lcd::setRW () { PORTD |= (1 << PD3); } + void Lcd::clrRW () { PORTD &= ~(1 << PD3); } + void Lcd::setE () { PORTC |= (1 << PC3); } + void Lcd::clrE () { PORTC &= ~(1 << PC3); } + + void Lcd::setData (uint8_t data) { + if (data & 0x01) PORTD |= (1 << PD4); else PORTD &= ~((1 << PD4)); + if (data & 0x02) PORTB |= (1 << PB0); else PORTB &= ~((1 << PB0)); + if (data & 0x04) PORTD |= (1 << PD7); else PORTD &= ~((1 << PD7)); + if (data & 0x08) PORTD |= (1 << PD6); else PORTD &= ~((1 << PD6)); + if (data & 0x10) PORTB |= (1 << PB2); else PORTB &= ~((1 << PB2)); + if (data & 0x20) PORTB |= (1 << PB3); else PORTB &= ~((1 << PB3)); + if (data & 0x40) PORTB |= (1 << PB4); else PORTB &= ~((1 << PB4)); + if (data & 0x80) PORTB |= (1 << PB5); else PORTB &= ~((1 << PB5)); + } + + void Lcd::dataDirectionOut () { + if (!mode4Bit) { + DDRB |= (1 << PB0); + DDRD |= ((1 << PD7) | (1 << PD6) | (1 << PD4)); + } + DDRB |= ((1 << PB5) | (1 << PB4) | (1 << PB3) | (1 << PB2)); + } + + void Lcd::dataDirectionIn () { + if (hardwareVersion == 1) { + // read back not allowed (missing level shifter 5V -> 3.3V) + dataDirectionOut(); + } else { + if (!mode4Bit) { + DDRB &= ~(1 << PB0); + DDRD &= ~((1 << PD7) | (1 << PD6) | (1 << PD4)); + } + DDRB &= ~((1 << PB5) | (1 << PB4) | (1 << PB3) | (1 << PB2)); + } + } + + uint8_t Lcd::getData () { + if (hardwareVersion == 1) { + // read back not allowed (missing level shifter 5V -> 3.3V) + _delay_ms(1); + return 0x00; // bit 8 (busy) = 0 + } else { + uint8_t b = 0; + b |= ((PIND & (1 << PD4)) != 0) << 0; + b |= ((PINB & (1 << PB0)) != 0) << 1; + b |= ((PIND & (1 << PD7)) != 0) << 2; + b |= ((PIND & (1 << PD6)) != 0) << 3; + b |= ((PINB & (1 << PB2)) != 0) << 4; + b |= ((PINB & (1 << PB3)) != 0) << 5; + b |= ((PINB & (1 << PB4)) != 0) << 6; + b |= ((PINB & (1 << PB5)) != 0) << 7; + return b; + } + } + + void Lcd::setBacklightOn () { + PORTC |= (1 << PC0); + } + + void Lcd::setBacklightOff () { + PORTC &= ~(1 << PC0); + } + +#endif + +// Befehle für das Display + +#define DISP_CLEAR 0b00000001 // Display clear +#define DISP_ON 0b00001111 // Display on +#define DISP_OFF 0b00001011 // Display off +#define CURSOR_ON 0b00001111 // Cursor on +#define CURSOR_OFF 0b00001101 // Cursor off +#define BLINK_ON 0b00001111 // Cursor Blink +#define BLINK_OFF 0b00001110 // Cursor No Blink + +#define LCD_PULSE_LENGTH 15 +#define LCD_CMD_DISPLAY_CLEAR 0x01 // Display clear +#define LCD_CMD_CURSOR_HOME 0x02 // Move cursor digit 1 +#define LCD_CMD_SET_ENTRY_MODE 0x04 // Entry Mode Set +#define LCD_CMD_DISPLAY_ON_OFF 0x08 // Display on/off +#define LCD_CMD_SHIFT 0x10 // Display shift +#define LCD_CMD_SET_MODE4BIT 0x20 // 4/8 Bits... +#define LCD_CMD_SET_CGRAM_ADDR 0x40 // Character Generator ROM +#define LCD_CMD_SET_DDRAM_ADDR 0x80 // Display Data RAM +#define LCD_BUSY_FLAG 0x80 + + + +int8_t Lcd::run (uint8_t subtest) { + if (subtest == 0) { + for (uint8_t i = 0; i < 20 * 4; i++) { + char c = (char)(i + 32); + if (i % 20 == 0) { + setCursor(i / 20, 0); + } + putChar(c); + waitOnReady(); + } + printf_P(PSTR("LCD ")); + if (status == 1) { + printf_P(PSTR("OK")); + } else { + printf_P(PSTR("ERROR(%d)"), status); + } + while (wait(1) == EOF) { + } + + } else { + printf_P(PSTR("end")); + return -1; + } + wait(500); + return 0; +} + + +void Lcd::initLcd () { + + setData(0x00); + dataDirectionOut(); + + _delay_ms(16); // min 15ms warten für Reset des Displays + status = 0; + for (uint8_t i = 0; i < 4; i++) { + if (mode4Bit) { + setRegister(LCD_CMD_SET_MODE4BIT | 0x08); // 4 Bit, 2/4 Zeilen, 5x7 + } else { + setRegister(0x08); // 8 Bit, 2 Zeilen, 5x7 + } + if (i == 0) { + _delay_ms(5); + } else { + _delay_us(100); + } + } + + setRegister(LCD_CMD_DISPLAY_ON_OFF | 0x04); // display on, cursor off + if (!isReady(50)) { + status = -1; + return; + } + + setRegister(LCD_CMD_DISPLAY_ON_OFF | 0x04); // display on, cursor off + if (!isReady(50)) { + status = -3; + return; + } + + setRegister(LCD_CMD_DISPLAY_CLEAR); + if (!isReady(1200)) { + status = -4; + return; + } + + status = 1; +} + +void Lcd::setRegister (uint8_t cmd) { + clrRW(); + clrRS(); + writeData(cmd); +} + +void Lcd::writeData (uint8_t data) { + clrE(); + dataDirectionOut(); + if (mode4Bit) { + setData(data & 0xf0); // send High-Nibble + setE(); + _delay_us(LCD_PULSE_LENGTH); + clrE(); + _delay_us(1); + setData(data << 4); // send Low-Nibble + } else { + setData(data); // send data byte + } + setE(); + _delay_us(LCD_PULSE_LENGTH); + clrE(); + _delay_us(1); +} + +void Lcd::setDRAddr (uint8_t address) { + waitOnReady(); + setRegister(LCD_CMD_SET_DDRAM_ADDR | address); + waitOnReady(); +} + +void Lcd::waitOnReady () { + if (isReady(50) == 0) { + status = -6; + } +} + +bool Lcd::isReady (uint16_t us) { + if (status < 0) { + return false; + } + if (hardwareVersion == 1) { + // read back not allowed (missing level shifter) + _delay_ms(1); + return true; + } + + uint8_t busy; + dataDirectionIn(); + setData(0xff); // enable internal pull up + + do { + uint8_t data = 0; + setRW(); + clrRS(); + + _delay_us(1); + setE(); + _delay_us(LCD_PULSE_LENGTH); + data = getData() & 0xf0; // High Nibble + clrE(); + + _delay_us(1); + setE(); + _delay_us(LCD_PULSE_LENGTH); + data |= getData() >> 4; // Low Nibble + + clrE(); + _delay_us(1); + clrRW(); + + busy = data & LCD_BUSY_FLAG; + us = (us >= 11) ? us - 11 : 0; + } while (us > 0 && busy); + + if (status == 1 && busy) { + status = -5; + } + + setData(0x00); + dataDirectionOut(); + + return busy == 0; +} + + +void Lcd::setDisplayOn () { + if (status != 1) { + return; + } + waitOnReady(); + setRegister(LCD_CMD_DISPLAY_ON_OFF | 0x04); // display on + waitOnReady(); +} + +void Lcd::setDisplayOff () { + if (status != 1) { + return; + } + waitOnReady(); + setRegister(LCD_CMD_DISPLAY_ON_OFF); // display off + waitOnReady(); +} + +void Lcd::clear () { + if (status != 1) { + return; + } + waitOnReady(); + setRegister(LCD_CMD_DISPLAY_CLEAR); + waitOnReady(); +} + +void Lcd::setCursor (uint8_t rowIndex, uint8_t columnIndex) { + if (status != 1 || columnIndex >= 20) { + return; + } + uint8_t b; + switch (rowIndex) { + case 0: b = 0x00 + columnIndex; break; + case 1: b = 0x40 + columnIndex; break; + case 2: b = 0x14 + columnIndex; break; + case 3: b = 0x54 + columnIndex; break; + default: return; + } + setDRAddr(b); + waitOnReady(); +} + +void Lcd::putChar (char c) { + if (status != 1) { + return; + } + clrRW(); + setRS(); + writeData(c); + clrRS(); + waitOnReady(); +} + +void Lcd::puts (const char * str) { + while (*str && status == 1) { + putChar(*str++); + } +} \ No newline at end of file diff --git a/software/deye-sun-12k/src/units/lcd.hpp b/software/deye-sun-12k/src/units/lcd.hpp new file mode 100644 index 0000000..47a30cc --- /dev/null +++ b/software/deye-sun-12k/src/units/lcd.hpp @@ -0,0 +1,48 @@ +#ifndef LCD_HPP +#define LCD_HPP + +#include +#include "../main.hpp" +#include + +class Lcd : public TestUnit { + public: + Lcd () { mode4Bit = hardwareVersion == 1 ? false : true; status = 0; }; + virtual void init (); + virtual void cleanup (); + virtual int8_t run (uint8_t subtest); + virtual PGM_P getName () { return PSTR("Lcd"); } + + private: + bool mode4Bit; + int8_t status; + + void initLcd (); + void setRegister (uint8_t cmd); + void setDRAddr (uint8_t address); + void writeData (uint8_t data); + void waitOnReady (); + bool isReady (uint16_t us); + void setDisplayOn (); + void setDisplayOff (); + void clear (); + void setCursor (uint8_t rowIndex, uint8_t columnIndex); + void putChar (char c); + void puts (const char * str); + + void setRS (); + void clrRS (); + void setRW (); + void clrRW (); + void setE (); + void clrE (); + uint8_t getData (); + void setData (uint8_t data); + void dataDirectionIn (); + void dataDirectionOut (); + void setBacklightOn (); + void setBacklightOff (); + +}; + +#endif \ No newline at end of file diff --git a/software/deye-sun-12k/src/units/led.cpp b/software/deye-sun-12k/src/units/led.cpp new file mode 100644 index 0000000..e908c21 --- /dev/null +++ b/software/deye-sun-12k/src/units/led.cpp @@ -0,0 +1,139 @@ +#include +#include +#include + +#include "led.hpp" +#include "../main.hpp" + +#if defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__) + + // Nano-644 / Nano 1284 + // --------------------------------------------------------------- + + // Nano-X-Base V1a V2a + // ----------------------- + // Red PD4 PD7 + // Orange/Yellow PD5 PD6 + // Green PD6 PD5 + // Blue PD7 PD4 + + void Led::init () { + PORTD &= ~((1 << PD7) | (1 << PD6) | (1 << PD5) | (1 << PD4)); + DDRD |= (1 << PD7) | (1 << PD6) | (1 << PD5) | (1 << PD4); + } + + void Led::cleanup () { + DDRD &= ~((1 << PD7) | (1 << PD6) | (1 << PD5) | (1 << PD4)); + PORTD &= ~((1 << PD7) | (1 << PD6) | (1 << PD5) | (1 << PD4)); + } + + void Led::setLed (LED led, bool on) { + if (on) { + switch(led) { + case RED: PORTD |= (1 << PD4); break; + case ORANGE: PORTD |= (1 << PD5); break; + case GREEN: PORTD |= (1 << PD6); break; + case BLUE: PORTD |= (1 << PD7); break; + } + } else { + switch(led) { + case RED: PORTD &= ~(1 << PD4); break; + case ORANGE: PORTD &= ~(1 << PD5); break; + case GREEN: PORTD &= ~(1 << PD6); break; + case BLUE: PORTD &= ~(1 << PD7); break; + } + } + } + + void Led::ledToggle (LED led) { + switch(led) { + case RED: PORTD ^= (1 << PD4); break; + case ORANGE: PORTD ^= (1 << PD5); break; + case GREEN: PORTD ^= (1 << PD6); break; + case BLUE: PORTD ^= (1 << PD7); break; + } + } + +#endif + +#ifdef __AVR_ATmega328P__ + + // Arduino-Nano-5V + // --------------------------------------------------------------- + + // Nano-X-Base V1a V2a + // ----------------------- + // Red PD5 PD2 + // Orange/Yellow PB1 PD3 + // Green PD3 PB1 + // Blue PD2 PD5 + + void Led::init () { + PORTD &= ~((1 << PD5) | (1 << PD3) | (1 << PD2)); + PORTB &= ~(1 << PB1); + DDRD |= (1 << PD5) | (1 << PD3) | (1 << PD2); + DDRB |= (1 << PB1); + } + + void Led::cleanup () { + DDRD &= ~((1 << PD5) | (1 << PD3) | (1 << PD2)); + DDRB &= ~(1 << PB1); + PORTD &= ~((1 << PD5) | (1 << PD3) | (1 << PD2)); + PORTB &= ~(1 << PB1); + } + + void Led::setLed (LED led, bool on) { + if (on) { + switch(led) { + case RED: PORTD |= (1 << PD5); break; + case ORANGE: PORTB |= (1 << PB1); break; + case GREEN: PORTD |= (1 << PD3); break; + case BLUE: PORTD |= (1 << PD2); break; + } + } else { + switch(led) { + case RED: PORTD &= ~(1 << PD5); break; + case ORANGE: PORTB &= ~(1 << PB1); break; + case GREEN: PORTD &= ~(1 << PD3); break; + case BLUE: PORTD &= ~(1 << PD2); break; + } + } + } + + void Led::ledToggle (LED led) { + switch(led) { + case RED: PORTD ^= (1 << PD5); break; + case ORANGE: PORTB ^= (1 << PB1); break; + case GREEN: PORTD ^= (1 << PD3); break; + case BLUE: PORTD ^= (1 << PD2); break; + } + } + +#endif + +void Led::ledOn (LED led) { + setLed(led, true); +} + +void Led::ledOff (LED led) { + setLed(led, false); +} + + +int8_t Led::run (uint8_t subtest) { + if (subtest <= 15) { + subtest = (subtest) % 4; + switch (subtest) { + case 0: ledOff(BLUE); ledOn(RED); break; + case 1: ledOff(RED); ledOn(ORANGE); break; + case 2: ledOff(ORANGE); ledOn(GREEN); break; + case 3: ledOff(GREEN); ledOn(BLUE); break; + } + printf_P(PSTR("Test LED D%d"), subtest + 1); + wait(500); + return 0; + } + + return -1; +} + diff --git a/software/deye-sun-12k/src/units/led.hpp b/software/deye-sun-12k/src/units/led.hpp new file mode 100644 index 0000000..780827f --- /dev/null +++ b/software/deye-sun-12k/src/units/led.hpp @@ -0,0 +1,25 @@ +#ifndef LED_HPP +#define LED_HPP + +#include +#include "../main.hpp" +#include + +class Led : public TestUnit { + public: + enum LED { RED, ORANGE, GREEN, BLUE }; + + public: + Led () {}; + virtual void init (); + virtual void cleanup (); + virtual int8_t run (uint8_t subtest); + virtual PGM_P getName () { return PSTR("Led"); } + + void setLed (LED led, bool on); + void ledOn (LED led); + void ledOff (LED led); + void ledToggle (LED led); +}; + +#endif \ No newline at end of file diff --git a/software/deye-sun-12k/src/units/modbus.cpp b/software/deye-sun-12k/src/units/modbus.cpp new file mode 100644 index 0000000..abbe36d --- /dev/null +++ b/software/deye-sun-12k/src/units/modbus.cpp @@ -0,0 +1,160 @@ +#include +#include +#include + +#include "modbus.hpp" +#include "../main.hpp" + + +#ifdef __AVR_ATmega328P__ +void Modbus::init () {} +void Modbus::cleanup () {} +int8_t Modbus::run (uint8_t subtest) { return -1; } +void Modbus::handleRxByte (uint8_t b) {} +#endif + +#if defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__) + +// PB0 ... nRE .. Read enable +// PB1 ... DE .. Data enable + +#define SET_nRE (PORTB |= (1 << PB0)) +#define CLR_nRE (PORTB &= ~(1 << PB0)) +#define SET_DE (PORTB |= (1 << PB1)) +#define CLR_DE (PORTB &= ~(1 << PB1)) + + +void Modbus::init () { +} + +void Modbus::cleanup () { + enabled = 0; + UCSR1A = 0; + UCSR1B = 0; + UCSR1C = 0; + UBRR1H = 0; + UBRR1L = 0; + PORTD &= ~(1 << PD2); + DDRB &= ~((1 << PB1) | (1 << PB0)); + PORTB &= ~((1 << PB1) | (1 << PB0)); +} + +int8_t Modbus::run (uint8_t subtest) { + if (subtest == 0) { + SET_nRE; + CLR_DE; + DDRB |= (1 << PB1) | (1 << PB0); + + // UART1 interface on Nano-644 + PORTD |= (1 << PD2); // enable RxD1 pullup + UCSR1A = (1 << U2X1); + UCSR1B = (1 << RXCIE1) | (1 << RXEN1) | (1 <= 1 && subtest <= 4) { + uint8_t nre, de, b; + switch (subtest) { + case 1: nre = 1; de = 0; b = 0x01; break; + case 2: nre = 1; de = 1; b = 0x8e; break; + case 3: nre = 0; de = 0; b = 0x55; break; + case 4: nre = 0; de = 1; b = 0xaa; break; + default: return -1; + } + printf_P(PSTR(" DE=%u, nRE=%u send 0x%02x... "), de, nre, b); + if (nre) { + SET_nRE; + } else { + CLR_nRE; + } + if (de) { + SET_DE; + } else { + CLR_DE; + } + _delay_us(100); + receivedBytes = 0; + UDR1 = b; + _delay_ms(1); + if (receivedBytes > 0) { + printf_P(PSTR("0x%02x received"), received[0]); + receivedBytes = 0; + } else { + printf_P(PSTR("no byte received")); + } + printf_P(PSTR(" ... press key to proceed")); + while (wait(0xffffffff) == EOF) {} + CLR_DE; + SET_nRE; + + } else if (subtest == 5) { + static uint8_t frame[] = { 0x01, 0x04, 0x00, 0x00, 0x00, 0x02, 0x71, 0xcb }; + printf_P(PSTR("Modbus: lese Spannung von Eastron SDM-230 (Einphasenzähler)")); + SET_DE; + CLR_nRE; + _delay_us(100); + do { + SET_DE; + receivedBytes = 0; + for (uint8_t i = 0; i < sizeof(frame); i++) { + UCSR1A |= (1 << TXC1); + UDR1 = frame[i]; + while ((UCSR1A & (1 < Sending:")); + for (uint8_t i = 0; i < sizeof(frame); i++) { + printf_P(PSTR(" 0x%02x"), frame[i]); + } + int k = wait(100); + + printf_P(PSTR("\n RxD1:")); + if (receivedBytes == 0) { + printf_P(PSTR("?")); + } else { + for (uint8_t i = 0; i < receivedBytes; i++) { + if (i == sizeof(frame)) { + printf_P(PSTR(" ")); + } + printf_P(PSTR(" 0x%02x"), received[i]); + } + } + if (receivedBytes >= 16) { + union { + uint8_t b[4]; + float value; + } f; + f.b[0] = received[14]; + f.b[1] = received[13]; + f.b[2] = received[12]; + f.b[3] = received[11]; + printf_P(PSTR(" -> %4.8fV\n"), (double)f.value); + } + if (k != EOF) { + break; + } + + } while (wait(1000) == EOF); + + } else { + printf_P(PSTR("end")); + return -1; + } + wait(500); + return 0; +} + +void Modbus::handleRxByte (uint8_t b) { + if (receivedBytes < sizeof(received)) { + received[receivedBytes++] = b; + } +} + +#endif \ No newline at end of file diff --git a/software/deye-sun-12k/src/units/modbus.hpp b/software/deye-sun-12k/src/units/modbus.hpp new file mode 100644 index 0000000..44b6a9d --- /dev/null +++ b/software/deye-sun-12k/src/units/modbus.hpp @@ -0,0 +1,24 @@ +#ifndef MODBUS_HPP +#define MODBUS_HPP + +#include +#include "../main.hpp" +#include + +class Modbus : public TestUnit { + public: + uint8_t enabled; + uint8_t receivedBytes; + uint8_t received[16]; + + + public: + Modbus () { enabled = 0; receivedBytes = 0; } + virtual void init (); + virtual void cleanup (); + virtual int8_t run (uint8_t subtest); + virtual PGM_P getName () { return PSTR("Modbus"); } + void handleRxByte (uint8_t); +}; + +#endif \ No newline at end of file diff --git a/software/deye-sun-12k/src/units/motor.cpp b/software/deye-sun-12k/src/units/motor.cpp new file mode 100644 index 0000000..7f8fbf9 --- /dev/null +++ b/software/deye-sun-12k/src/units/motor.cpp @@ -0,0 +1,221 @@ +#include +#include +#include + +#include "motor.hpp" +#include "../main.hpp" + +#if defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__) + + // Nano-644 + // --------------------------------------------------------------- + // PB0 ..... rotation-sensor + // PB2 ..... nFault + // PB3 ..... PWM (OC0A) + // PB4 ..... EN + // PA3 ..... SW3 -> push button for Motor enable control + + #define ADC0K 64 + + void Motor::init () { + DDRD |= (1 << PD7); // sensor signal toggle on PD7 (LED D4 red) + ADMUX = (1 << ADLAR) | (1 << REFS0); // ADC0, VREF=AVCC=3.3V + ADCSRA = (1 << ADEN) | 7; // ADC Enable, Prescaler 128 + TCCR0A = (1 << COM0A1) | (1 << WGM01) | (1 << WGM00); // Fast PWM on OC0A + // TCCR0B = (1 << CS02) | ( 1 << CS00); // f = 12 MHz / 1024 = 11,71875 kHz -> fPWM=45Hz + TCCR0B = (1 << CS02); // f = 12 MHz / 256 = 46,875 kHz -> fPWM=183,1Hz + DDRB |= (1 << PB4) | (1 << PB3); // Motor enable + PORTA |= (1 << PORTA3); // push button for Motor enable control + setEnable(); + enabled = 1; + } + + void Motor::cleanup () { + DDRD &= ~(1 << PD7); + ADMUX = 0; + ADCSRA = 0; + TCCR0A = 0; + TCCR0B = 0; + DDRB &= ~((1 << PB4) | (1 << PB3)); + PORTA &= ~(1 << PORTA3); + enabled = 0; + } + + bool Motor::isSW3Pressed () { + return (PINA & (1 << PC3)) == 0; + } + + void Motor::clearEnable () { + PORTB &= ~(1 << PB4); + } + + void Motor::setEnable () { + PORTB |= (1 << PB4); + } + + bool Motor::isFaultLow () { + return (PINB & (1 << PB2)) == 0; + } + + bool Motor::isSensorHigh () { + return (PINB & (1 << PB0)) != 0; + } + + void Motor::toggleD4 () { + PORTD ^= (1 << PD7); + } + +#endif + +#ifdef __AVR_ATmega328P__ + + // Arduino-Nano-5V + // --------------------------------------------------------------- + // PD4 ..... rotation-sensor + // PD7 ..... nFault + // PD6/OC0A ..... PWM + // PB2 ..... EN + // PC3 ..... SW3 -> push button for Motor enable control + + #define ADC0K 91 + + void Motor::init () { + DDRD |= (1 << PD2); // sensor signal toggle on PD2 (LED D4 red) + ADMUX = (1 << ADLAR) | (1 << REFS0); // ADC0, VREF=AVCC=5V + ADCSRA = (1 << ADEN) | 7; // ADC Enable, Prescaler 128 + TCCR0A = (1 << COM0A1) | (1 << WGM01) | (1 << WGM00); // Fast PWM on OC0A + // TCCR0B = (1 << CS02) | ( 1 << CS00); // f = 16 MHz / 1024 = 15,625 kHz -> fPWM=61.04Hz + TCCR0B = (1 << CS02); // f = 16 MHz / 256 = 62.5 kHz -> fPWM=244.14Hz + DDRB |= (1 << PB2); + DDRC &= ~(1 << PC3); + DDRD |= (1 << PD6); + DDRD &= ~((1 << PD4) | (1 << PD7)); + PORTC |= ( 1 << PC3); + PORTD |= (1 << PD7) | (1 << PD5); + setEnable(); + enabled = 1; + } + + void Motor::cleanup () { + DDRD &= ~(1 << PD2); + enabled = 0; + ADMUX = 0; + ADCSRA = 0; + TCCR0A = 0; + TCCR0B = 0; + clearEnable(); + DDRB &= ~((1 << PB2)); + DDRC &= ~(1 << PC3); + DDRD &= ~((1 << PD7) | (1 << PD6) | (1 << PD4)); + PORTC &= ~( 1 << PC3); + PORTD &= ~((1 << PD7) | (1 << PD6)); + } + + bool Motor::isSW3Pressed () { + return (PINC & (1 << PC3)) == 0; + } + + void Motor::clearEnable () { + PORTB &= ~(1 << PB2); + } + + void Motor::setEnable () { + PORTB |= (1 << PB2); + } + + bool Motor::isFaultLow () { + return (PIND & (1 << PD7)) == 0; + } + + bool Motor::isSensorHigh () { + return (PIND & (1 << PD4)) != 0; + } + + void Motor::toggleD4 () { + PORTD ^= (1 << PD2); + } + +#endif + +int8_t Motor::run (uint8_t subtest) { + switch (subtest) { + case 0: { + printf_P(PSTR("\n")); + while (wait(10) == EOF) { + + printf_P(PSTR("\r SW3=%d->"), isSW3Pressed() ? 0 : 1); + if (isSW3Pressed()) { + clearEnable(); + printf_P(PSTR("EN=0")); + } else { + setEnable(); + printf_P(PSTR("EN=1")); + } + + ADCSRA |= (1 << ADSC); // start ADC + while (ADCSRA & (1 << ADSC)) {} // wait for result + printf_P(PSTR("\r => ADC0=%3d"), ADCH); + + ADMUX = (1 << ADLAR) | (1 << REFS1) | (1 << REFS0) | 2; // ADC2, VREF=2.5V + + int16_t x = ((int16_t)(ADCH) - 5) * ADC0K / 64; + if (x < 0) x = 0; else if (x > 255) x = 255; + uint8_t dutyCycle = 0xff - (uint8_t)x; + if (dutyCycle <= 1) { + dutyCycle = 0; + } else if (dutyCycle > 254) { + dutyCycle = 255; + } + OCR0A = dutyCycle; + printf_P(PSTR(" PWM/OC0A=%3d"), dutyCycle); + + ADCSRA |= (1 << ADSC); // start ADC + while (ADCSRA & (1 << ADSC)) {} // wait for result + printf_P(PSTR(" ADC2=%3d"), ADCH); + ADMUX = (1 << ADLAR) | (1 << REFS0); // ADC0, VREF=AVCC=3.3V + + printf_P(PSTR(" nFAULT=%d"), isFaultLow() ? 0 : 1); + printf_P(PSTR(" SENSOR=%d "), isSensorHigh()); + uint16_t timer; + ATOMIC_BLOCK(ATOMIC_FORCEON) { + timer = rpmTimer; + } + float rpm = 60.0 / (float)timer / 0.0001; + if (timer > 0) { + printf_P(PSTR(" n= %5d U/min (T=%04x)"), (int)rpm, timer); + } else { + printf_P(PSTR(" no rotation (T=%04x) "), timer); + } + + } + return 0; + } + } + + return -1; +} + +void Motor::tick100us () { + static uint16_t timerH = 0; + static uint16_t timerL = 0; + static bool lastSensorHigh = false; + + bool sensorHigh = isSensorHigh(); + if (!sensorHigh && sensorHigh != lastSensorHigh && timerL > 2) { + rpmTimer = timerL + timerH; + timerL = 0; + timerH = 0; + toggleD4(); + } + if (sensorHigh) { + timerH = timerH < 0x4000 ? timerH + 1 : 0x4000; + } else { + timerL = timerL < 0x4000 ? timerL + 1 : 0x4000; + } + if (timerH >= 0x4000 || timerL >= 0x4000) { + rpmTimer = 0; // no ratation detected + } + lastSensorHigh = sensorHigh; +} + + diff --git a/software/deye-sun-12k/src/units/motor.hpp b/software/deye-sun-12k/src/units/motor.hpp new file mode 100644 index 0000000..2cbd15a --- /dev/null +++ b/software/deye-sun-12k/src/units/motor.hpp @@ -0,0 +1,30 @@ +#ifndef MOTOR_HPP +#define MOTOR_HPP + +#include +#include "../main.hpp" +#include + +class Motor : public TestUnit { + public: + uint8_t enabled; + uint16_t rpmTimer; + + public: + Motor () { enabled = 0; rpmTimer = 0; }; + virtual void init (); + virtual void cleanup (); + virtual int8_t run (uint8_t subtest); + virtual PGM_P getName () { return PSTR("Motor"); } + void tick100us (); + + private: + bool isSW3Pressed (); + void clearEnable (); + void setEnable (); + bool isFaultLow (); + bool isSensorHigh (); + void toggleD4 (); +}; + +#endif \ No newline at end of file diff --git a/software/deye-sun-12k/src/units/portexp.cpp b/software/deye-sun-12k/src/units/portexp.cpp new file mode 100644 index 0000000..a153589 --- /dev/null +++ b/software/deye-sun-12k/src/units/portexp.cpp @@ -0,0 +1,195 @@ +#include +#include +#include + +#include "portexp.hpp" +#include "../main.hpp" + +// Port-Expander MCP23S17 + +// SN-Print Stecker IO16 +// MCP23S17 | IO16 (MEGA2560) | Ampel-Print | | MCP23S17 | IO16 (MEGA2560) | Ampel-Print | +// ------------------------------------------ -------------------------------------------- +// GPA0 | IO16O7 (PA7) | Taster RU | | GPB0 | IO16U7 (PC7) | Taster LU | +// GPA1 | IO16O6 (PA6) | Taster RO | | GPB1 | IO16U6 (PC6) | Taster LO | +// GPA2 | IO16O5 (PA5) | U-Gruen | | GPB2 | IO16U5 (PC5) | R-Gruen | +// GPA3 | IO16O4 (PA4) | U-Gelb | | GPB3 | IO16U4 (PC4) | R-Gelb | +// GPA4 | IO16O3 (PA3) | U-Rot | | GPB4 | IO16U3 (PC3) | R-Rot | +// GPA5 | IO16O2 (PA2) | L-Gruen | | GPB5 | IO16U2 (PC2) | O-Gruen | +// GPA6 | IO16O1 (PA1) | L-Gelb | | GPB6 | IO16U1 (PC1) | O-Gelb | +// GPA7 | IO16O0 (PA0) | L-Rot | | GPB7 | IO16U0 (PC0) | O-Rot | + +#if defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__) + + // Nano-644 / Nano1284 + // -------------------------------------------------------- + // PA7 ... nCS + // PB5 ... MOSI + // PB6 ... MISO + // PB7 ... SCK + + void PortExp::init () { + PRR0 &= (1 << PRSPI); + PORTA |= (1 << PA7); + DDRA |= (1 << PA7); // SPI nCS + // PORTB/DDRB must be configured before SPCR !! + PORTB |= (1 << PB4); // nSS must be HIGH, otherwise SPI master will not become active!! + DDRB |= (1 << PB7) | (1 << PB5) | (1 << PB4); // SPI SCK (=PB7) and SPI MOSI (=PB5) + DDRB &= ~(1 << PB6); + + SPCR |= (1 << SPE) | (1 << MSTR) | (1 << SPR1) | (1 << SPR0); // SPI enable , Master, f=12MHz/128=93,75kHz + // SPCR = (1 << SPE) | (1 << MSTR); // SPI enable , Master, f=12MHz/4 = 3MHz + } + + void PortExp::cleanup () { + DDRB &= ~(1 << PB6); // // SPI MISO (=PB6) + DDRB &= ~((1 << PB7) | (1 << PB5)); // SPI SCK (=PB7) and SPI MOSI (=PB5) + DDRA &= ~(1 << PA7); + PORTA &= ~(1 << PA7); // SPI nCS + SPCR = 0; + } + + void PortExp::setChipEnable () { + PORTA &= ~(1 << PA7); + } + + void PortExp::clearChipEnable () { + PORTA |= (1 << PA7); + } + + +#endif + +#ifdef __AVR_ATmega328P__ + + // Arduino-Nano-5V + // ------------------------------------ + // PC1 ... nCS (MANUAL (!) connection PA1 - PA7 required) + // PB3 ... MOSI + // PB4 ... MISO + // PB5 ... SCK + + void PortExp::init () { + PRR &= (1 << PRSPI); + PORTC |= (1 << PC1); + DDRC |= (1 << PC1); // SPI nCS + // PORTB/DDRB must be configured before SPCR !! + PORTB |= (1 << PB2); // nSS must be HIGH, otherwise SPI master will not become active!! + DDRB |= (1 << PB5) | (1 << PB3) | (1 << PB2); // SPI SCK (=PB5), SPI MOSI (=PB3), SPI nSS (=PB2) + + SPCR |= (1 << SPE) | (1 << MSTR) | (1 << SPR1) | (1 << SPR0); // SPI enable , Master, f=16MHz/128=125kHz + // SPCR = (1 << SPE) | (1 << MSTR); // SPI enable , Master, f=12MHz/4 = 3MHz + + if (hardwareVersion == 2) { + PORTD |= (1 << PD7); // nCS when V2a/JP39.2-3 jumpered + DDRD |= (1 << PD7); + } + } + + void PortExp::cleanup () { + PORTC &= ~(1 << PC1); + DDRC &= ~(1 << PC1); + PORTB &= ~(1 << PB2); + DDRB &= ~((1 << PB5) | (1 << PB3) | (1 << PB2)); + SPCR = 0; + } + + void PortExp::setChipEnable () { + if (hardwareVersion == 2) { + PORTD &= ~(1 << PD7); + } + } + + void PortExp::clearChipEnable () { + if (hardwareVersion == 2) { + PORTD |= (1 << PD7); + } + } + +#endif + + + +int8_t PortExp::writeByte (uint8_t addr, uint8_t b) { + // no response via SPI MISO because SO stays tristate on write cycle + setChipEnable(); + SPDR = 0x40; // WRITE BYTE + while ((SPSR & (1 << SPIF)) == 0) {} + SPDR = addr; // register address + while ((SPSR & (1 << SPIF)) == 0) {} + SPDR = b; // value + while ((SPSR & (1 << SPIF)) == 0) {} + clearChipEnable(); + + _delay_us(5); + return 0; +} + +uint8_t PortExp::readByte (uint8_t addr) { + // response via SPI MISO only on third byte + setChipEnable(); + SPDR = 0x41; // write "READ BYTE" + while ((SPSR & (1 << SPIF)) == 0) {} + SPDR = addr; // write "register address" + while ((SPSR & (1 << SPIF)) == 0) {} + SPDR = 0; // additional 8 clocks to get response from port expander + while ((SPSR & (1 << SPIF)) == 0) {} + clearChipEnable(); + return SPDR; +} + +void PortExp::checkResponse (uint8_t address, uint8_t response, uint8_t desired) { + printf_P(PSTR(" (read 0x%02x -> 0x%02x"), address, response); + if (response != desired) { + printf_P(PSTR(" ERROR")); + if (response == 0xff) { + printf_P(PSTR(" JP39.2/3 jumpered (left)?")); + } + } else { + printf_P(PSTR(" OK")); + } + printf_P(PSTR(")")); +} + + +int8_t PortExp::run (uint8_t subtest) { + #ifdef __AVR_ATmega328P__ + if (hardwareVersion == 1) { + printf_P(PSTR("ERROR - nCS not controlable\n")); + return -1; + } + #endif + if (subtest == 0) { + while (wait(500) == EOF) { + printf_P(PSTR("\n => start ...")); + for (uint8_t i = 0; i < 8; i++) { + writeByte(0, ~(1 << i)); // IODIRA (Bank = 0) + writeByte(0x12, (1 << i)); // GPIOA (Bank = 0) + printf_P(PSTR("\n Bank0 - GPA%d = 1"), i); + checkResponse (0x12, readByte(0x12), (1 << i)); + wait(200); + writeByte(0x12, 0); // GPIOA (Bank = 0) + printf_P(PSTR("\n Bank0 - GPA%d = 0"), i); + checkResponse (0x12, readByte(0x12), 0); + writeByte(0, 0xff); // IODIRA (Bank = 0) + wait(200); + } + for (uint8_t i = 0; i < 8; i++) { + writeByte(1, ~(1 << i)); // IODIRB (Bank = 0) + writeByte(0x13, (1 << i)); // GPIOB (Bank = 0) + printf_P(PSTR("\n Bank0 - GPB%d = 1"), i); + checkResponse (0x13, readByte(0x13), (1 << i)); + wait(200); + writeByte(0x13, 0); // GPIOB (Bank = 0) + printf_P(PSTR("\n Bank0 - GPB%d = 0"), i); + checkResponse (0x13, readByte(0x13), 0); + writeByte(1, 0xff); // IODIRB (Bank = 0) + wait(200); + } + } + return 0; + } + + return -1; +} + diff --git a/software/deye-sun-12k/src/units/portexp.hpp b/software/deye-sun-12k/src/units/portexp.hpp new file mode 100644 index 0000000..8705662 --- /dev/null +++ b/software/deye-sun-12k/src/units/portexp.hpp @@ -0,0 +1,24 @@ +#ifndef PORTEXP_HPP +#define PORTEXP_HPP + +#include +#include "../main.hpp" +#include + +class PortExp : public TestUnit { + public: + PortExp () {}; + virtual void init (); + virtual void cleanup (); + virtual int8_t run (uint8_t subtest); + virtual PGM_P getName () { return PSTR("PortExp"); } + + private: + void setChipEnable (); + void clearChipEnable (); + int8_t writeByte (uint8_t addr, uint8_t b); + uint8_t readByte (uint8_t addr); + void checkResponse (uint8_t address, uint8_t response, uint8_t desired); +}; + +#endif \ No newline at end of file diff --git a/software/deye-sun-12k/src/units/poti.cpp b/software/deye-sun-12k/src/units/poti.cpp new file mode 100644 index 0000000..94fc5a4 --- /dev/null +++ b/software/deye-sun-12k/src/units/poti.cpp @@ -0,0 +1,34 @@ +#include +#include + +#include "poti.hpp" +#include "../main.hpp" + +void Poti::init () { + ADMUX = (1 << REFS0); // ADC0, VREF=AVCC=3.3V + ADCSRA = (1 << ADEN) | 7; // ADC Enable, Prescaler 128 +} + +void Poti::cleanup () { + ADMUX = 0; + ADCSRA = 0; +} + +int8_t Poti::run (uint8_t subtest) { + switch (subtest) { + case 0: { + printf_P(PSTR("\n")); + while (wait(10) == EOF) { + printf_P(PSTR("\r => Measure ADC0: ")); + ADCSRA |= (1 << ADSC); // start ADC + while (ADCSRA & (1 << ADSC)) {} // wait for result + printf_P(PSTR("%4d (0x%03x)"), ADC, ADC); + } + return 0; + } + } + + return -1; +} + + diff --git a/software/deye-sun-12k/src/units/poti.hpp b/software/deye-sun-12k/src/units/poti.hpp new file mode 100644 index 0000000..b13dd29 --- /dev/null +++ b/software/deye-sun-12k/src/units/poti.hpp @@ -0,0 +1,17 @@ +#ifndef POTI_HPP +#define POTI_PP + +#include +#include "../main.hpp" +#include + +class Poti : public TestUnit { + public: + Poti () {}; + virtual void init (); + virtual void cleanup (); + virtual int8_t run (uint8_t subtest); + virtual PGM_P getName () { return PSTR("Poti"); } +}; + +#endif \ No newline at end of file diff --git a/software/deye-sun-12k/src/units/r2r.cpp b/software/deye-sun-12k/src/units/r2r.cpp new file mode 100644 index 0000000..54664b1 --- /dev/null +++ b/software/deye-sun-12k/src/units/r2r.cpp @@ -0,0 +1,48 @@ +#include +#include + +#include "r2r.hpp" +#include "../main.hpp" + +#if defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__) + // AVCC=3.3, POTI Vmax=3.3V + #define K 1.0 +#endif + +#ifdef __AVR_ATmega328P__ + // AVCC=4.7V, POTI Vmax=3.3V + #define K 1.32 +#endif + +void R2r::init () { + ADMUX = (1 << REFS0) | 2; // ADC2, VREF=AVCC (=3.3V for Nano-644/1284, =5V for Arduino Nano) + ADCSRA = (1 << ADEN) | 7; // ADC Enable, Prescaler 128 +} + +void R2r::cleanup () { + ADMUX = 0; + ADCSRA = 0; +} + +int8_t R2r::run (uint8_t subtest) { + switch (subtest) { + case 0: { + printf_P(PSTR("\n")); + while (wait(10) == EOF) { + printf_P(PSTR("\r => Measure ADC2: ")); + ADCSRA |= (1 << ADSC); // start ADC + while (ADCSRA & (1 << ADSC)) {} // wait for result + printf_P(PSTR("%4d (0x%03x)"), ADC, ADC); + // uint8_t sw = (uint8_t)( ((float)(ADC) + 4.0) / 64.0 * K ); + float swf = ((float)(ADC) * K) / 64.8 + 0.55; + uint8_t sw = (uint8_t)swf ; + printf_P(PSTR(" %3.1f => SW9:6 = %d %d% d %d "), swf, sw >> 3, (sw >> 2) & 0x01, (sw >> 1) & 0x01, sw & 0x01 ); + } + return 0; + } + } + + return -1; +} + + diff --git a/software/deye-sun-12k/src/units/r2r.hpp b/software/deye-sun-12k/src/units/r2r.hpp new file mode 100644 index 0000000..84e97e6 --- /dev/null +++ b/software/deye-sun-12k/src/units/r2r.hpp @@ -0,0 +1,17 @@ +#ifndef R2R_HPP +#define R2R_PP + +#include +#include "../main.hpp" +#include + +class R2r : public TestUnit { + public: + R2r () {}; + virtual void init (); + virtual void cleanup (); + virtual int8_t run (uint8_t subtest); + virtual PGM_P getName () { return PSTR("R2R"); } +}; + +#endif \ No newline at end of file diff --git a/software/deye-sun-12k/src/units/rgb.cpp b/software/deye-sun-12k/src/units/rgb.cpp new file mode 100644 index 0000000..3f69cbd --- /dev/null +++ b/software/deye-sun-12k/src/units/rgb.cpp @@ -0,0 +1,152 @@ +#include +#include + +#include "rgb.hpp" +#include "../main.hpp" + +#if defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__) + + // Nano-644 + // --------------------------------------------------------------- + // PB0 ..... Red (inverse logic -> 0 = ON) + // PB1 ..... Green (inverse logic -> 0 = ON) + // PB2 ..... Blue (inverse logic -> 0 = ON) + + void Rgb::init () { + ledOff(RED); + ledOff(GREEN); + ledOff(BLUE); + DDRB |= (1 << PB2) | (1 << PB1) | (1 << PB0); + } + + void Rgb::cleanup () { + ledOff(RED); + ledOff(GREEN); + ledOff(BLUE); + DDRB &= ~((1 << PB2) | (1 << PB1) | (1 << PB0)); + } + + void Rgb::setLed (LED led, bool on) { + if (on) { + switch(led) { + case RED: PORTB &= ~(1 << PB0); break; + case GREEN: PORTB &= ~(1 << PB1); break; + case BLUE: PORTB &= ~(1 << PB2); break; + } + } else { + switch(led) { + case RED: PORTB |= (1 << PB0); break; + case GREEN: PORTB |= (1 << PB1); break; + case BLUE: PORTB |= (1 << PB2); break; + } + } + } + + void Rgb::ledToggle (LED led) { + switch(led) { + case RED: PORTB ^= (1 << PB0); break; + case GREEN: PORTB ^= (1 << PB1); break; + case BLUE: PORTB ^= (1 << PB2); break; + } + } + + +#endif + +#ifdef __AVR_ATmega328P__ + + // Arduino-Nano-5V + // --------------------------------------------------------------- + // PD4 ..... Red (inverse logic -> 0 = ON) + // PB0 ..... Green (inverse logic -> 0 = ON) + // PD7 ..... Blue (inverse logic -> 0 = ON) + + void Rgb::init () { + ledOff(RED); + ledOff(GREEN); + ledOff(BLUE); + DDRB |= (1 << PB0); + DDRD |= (1 << PD7) | (1 << PD4); + } + + void Rgb::cleanup () { + ledOff(RED); + ledOff(GREEN); + ledOff(BLUE); + DDRB &= ~(1 << PB0); + DDRD &= ~((1 << PD7) | (1 << PD4)); + } + + void Rgb::setLed (LED led, bool on) { + if (on) { + switch (led) { + case RED: PORTD &= ~(1 << PD4); break; + case GREEN: PORTB &= ~(1 << PB0); break; + case BLUE: PORTD &= ~(1 << PD7); break; + } + } else { + switch (led) { + case RED: PORTD |= (1 << PD4); break; + case GREEN: PORTB |= (1 << PB0); break; + case BLUE: PORTD |= (1 << PD7); break; + } + } + } + + void Rgb::ledToggle (LED led) { + switch (led) { + case RED: PORTD ^= (1 << PD4); break; + case GREEN: PORTB ^= ~(1 << PB0); break; + case BLUE: PORTD ^= (1 << PD7); break; + } + } + +#endif + +void Rgb::ledOn (LED led) { + setLed(led, true); +} + +void Rgb::ledOff (LED led) { + setLed(led, false); +} + +int8_t Rgb::run (uint8_t subtest) { + switch (subtest) { + case 0: { + ledOn(RED); + printf_P(PSTR("Red")); + wait(3000); + ledOff(RED); + return 0; + } + + case 1: { + ledOn(GREEN); + printf_P(PSTR("Green")); + wait(3000); + ledOff(GREEN); + return 0; + } + + case 2: { + ledOn(BLUE); + printf_P(PSTR("Blue")); + wait(3000); + ledOff(BLUE); + return 0; + } + + case 3: { + ledOn(RED); ledOn(GREEN); ledOn(BLUE); + printf_P(PSTR("All")); + wait(3000); + ledOff(RED); ledOff(GREEN); ledOff(BLUE); + return 0; + } + } + + return -1; +} + + diff --git a/software/deye-sun-12k/src/units/rgb.hpp b/software/deye-sun-12k/src/units/rgb.hpp new file mode 100644 index 0000000..12e9da4 --- /dev/null +++ b/software/deye-sun-12k/src/units/rgb.hpp @@ -0,0 +1,25 @@ +#ifndef RGB_HPP +#define RGB_PP + +#include +#include "../main.hpp" +#include + +class Rgb : public TestUnit { + public: + enum LED { RED, GREEN, BLUE }; + + public: + Rgb () {}; + virtual void init (); + virtual void cleanup (); + virtual int8_t run (uint8_t subtest); + virtual PGM_P getName () { return PSTR("Rgb"); } + + void setLed (LED led, bool on); + void ledOn (LED led); + void ledOff (LED led); + void ledToggle (LED led); +}; + +#endif \ No newline at end of file diff --git a/software/deye-sun-12k/src/units/rtc8563.cpp b/software/deye-sun-12k/src/units/rtc8563.cpp new file mode 100644 index 0000000..6ae6497 --- /dev/null +++ b/software/deye-sun-12k/src/units/rtc8563.cpp @@ -0,0 +1,253 @@ +#include +#include +#include +#include +#include + +#include "rtc8563.hpp" +#include "../adafruit/bme280.h" +#include "../main.hpp" + +// RTC BME653EMA on Nano-644 + +const char PSTR_WEEKDAYS[] PROGMEM = "So\0Mo\0Di\0Mi\0Do\0Fr\0Sa\0"; + +// const uint8_t CONFIG[] PROGMEM = { +// /* config -> */ 0x00, 0x00, +// /* enable nINT -> */ 0x01, 0x01, // TIE = 1 +// /* set clock -> */ 0x02, 0x00, 0x00, 0x08, 0x16, 0x02, 0x08, 0x24 +// }; + +void Rtc8563::handleTwiIrq () { + TWCR |= (1 << TWINT); // clear Interrupt Request +} + +#ifdef __AVR_ATmega328P__ +void Rtc8563::init () {} +void Rtc8563::cleanup () {} +int8_t Rtc8563::run (uint8_t subtest) { return -1; } +PGM_P Rtc8563::getName () { return PSTR("?"); } +#endif + +#if defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__) + +void Rtc8563::init () { + PORTC &= ~(1 << PC7); // nInt and nPowerOn (Q7 -> BATTERY) + DDRC |= (1 << PC7); + TWBR = 13; // 100kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 100000) / (2 * 100000 * 4); + TWBR = 28; // 50kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 50000) / (2 * 50000 * 4); + TWBR = 100; // 50kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 50000) / (2 * 50000 * 4); + TWCR = (1 << TWEN); + enabled = true; +} + +void Rtc8563::cleanup () { + enabled = false; + TWCR = (1 << TWEN); + TWBR = 0; + // PORTC &= ~(1 << PC7); + // DDRC &= ~(1 << PC7); +} + +PGM_P Rtc8563::getName () { + return PSTR("RTC-8563"); +} + +uint8_t Rtc8563::bcd2bin (uint8_t value) { + return (value >> 4) * 10 + (value & 0x0f); +} + +int8_t Rtc8563::run (uint8_t subtest) { + int key = EOF; + if (subtest == 0) { + // printf_P(PSTR(" BM280 ... ")); + rtc8563.begin(0x51); + Clock_t clock; + // uint8_t bufferConfig[] = { 0x00, 0x00, 0x01, 0x01 }; + // uint8_t bufferSetClock[] = { 0x02, 0x00, 0x00, 0x08, 0x16, 0x02, 0x08, 0x24 }; + + Rtc8563Reg_t reg; + uint8_t *pReg = (uint8_t *)(void *)® + memset(®, 0, sizeof(reg)); + + printf_P(PSTR("\n => config 8563 ... ")); + reg.reg1.field.tie = 1; + if (!rtc8563.write(pReg, 2)) { + printf_P(PSTR_ERROR); + } else { + printf_P(PSTR_Done); + } + printf_P(PSTR("\n press:")); + printf_P(PSTR("\n t .... timer on/off")); + printf_P(PSTR("\n p .... power on/off (PC7->Q1)")); + printf_P(PSTR("\n c .... init clock")); + printf_P(PSTR("\n w/W .. weekday (+/-)\n")); + printf_P(PSTR("\n y/Y .. year (+/-)")); + printf_P(PSTR("\n m/M .. month (+/-)")); + printf_P(PSTR("\n d/D .. day (+/-)")); + printf_P(PSTR("\n h/H .. hour (+/-)")); + printf_P(PSTR("\n n/N .. minute (+/-)")); + printf_P(PSTR("\n s/S .. second (+/-)\n")); + + do { + uint8_t addr = 0x00; + printf_P(PSTR("\n => read register 0-15 (hex):")); + if (!rtc8563.write_then_read(&addr, 1, pReg, sizeof(reg))) { + printf_P(PSTR_ERROR); + key = waitAndReadKey(1000); + continue; + } + memccpy(&clock, ®.clock, sizeof(clock), sizeof(clock)); + for (uint8_t i = 0; i < 16; i++) { + if (i % 4 == 0) { + printf_P(PSTR(" ")); + } + printf_P(PSTR(" %02X"), pReg[i]); + } + uint16_t year = (clock.month.field.century ? 2100 : 2000) + bcd2bin(clock.year.byte); + int8_t month = bcd2bin(clock.month.byte); + int8_t day = bcd2bin(clock.day.byte); + int8_t hrs = bcd2bin(clock.hour.byte); + int8_t min = bcd2bin(clock.min.byte); + int8_t sec = bcd2bin(clock.sec.byte); + int8_t weekday = clock.weekday.byte; + + PGM_P d = weekday >= 0 && weekday < 7 ? &PSTR_WEEKDAYS[weekday * 3] : PSTR("??"); + printf_P(PSTR(" --> ")); + printf_P(d); + printf_P(PSTR(", %d %04d-%02d-%02d %02d:%02d:%02d"), weekday, year, month, day, hrs, min, sec); + printf_P(PSTR(" - Timer=0x%02x"), reg.timer); + + key = waitAndReadKey(1000); + bool ok = true; + bool change = false; + switch (key) { + case 't': ok &= setTimer(reg.timerControl.field.enable ? 0 : 10, reg); break; + case 'c': ok &= setClock(5, 2024,8,16, 17,12,10 ); break; // ok &= rtc8563.write(bufferSetClock, sizeof(bufferSetClock)); break; + case 'p': powerOnOff(10, reg); break; + case 'y': year++; change = true; break; + case 'Y': year--; change = true; break; + case 'm': month++; change = true; break; + case 'M': month--; change = true; break; + case 'd': day++; change = true; break; + case 'D': day--; change = true; break; + case 'h': hrs++; change = true; break; + case 'H': hrs--; change = true; break; + case 'n': min++; change = true; break; + case 'N': min--; change = true; break; + case 's': sec++; change = true; break; + case 'S': sec--; change = true; break; + case 'w': weekday++; change = true; break; + case 'W': weekday--; change = true; break; + } + if (change) { + printf_P(PSTR("\n set: %04d-%02d-%02d %02d:%02d:%02d"), year, month, day, hrs, min, sec); + setClock(weekday, year, month, day, hrs, min, sec); + } + + } while (key != ESCAPE); + + return 0; + } + + return -1; +} + +bool Rtc8563::setTimer (uint8_t seconds, Rtc8563Reg_t ®) { + + reg.timerControl.field.fd = FDTIMER_1HZ; + reg.timerControl.field.enable = seconds > 0; + reg.timer = seconds; + reg.reg1.field.tie = seconds > 0; + // clear and alarm flag behavior on I2C write different to datasheet + // datasheet: tf cleared to 0, af remains unchanged + // realchip: tf remains 1 and af is set to 1 (no negative result because tie=0 and aie=0) + reg.reg1.field.tf = 0; // clear timer flag + reg.reg1.field.af = 1; // alarm flag remains unchanged + if (seconds > 0) { + printf_P(PSTR("\n Timer set to %ds (1:%02X) ... "), seconds, reg.reg1.byte); + } else { + printf_P(PSTR("\n Timer off ... ")); + } + if (rtc8563.writeByteAndBuffer(0x01, ®.reg1.byte, 1) && rtc8563.writeByteAndBuffer(14, ®.timerControl.byte, 2)) { + printf_P(PSTR("OK")); + return true; + } else { + printf_P(PSTR("fails")); + return false; + } +} + +bool Rtc8563::powerOnOff (uint8_t delayOffSeconds, Rtc8563Reg_t ®) { + int key = EOF; + if (PORTC & (1 << PC7)) { + printf_P(PSTR("\n power on ...")); + DDRC |= ( 1<< PC7); + PORTC &= ~(1 << PC7); + setTimer(0, reg); + } else { + printf_P(PSTR("\n")); + key = EOF; + for (int8_t i = 9; i > 0 && key == EOF; i--) { + printf_P(PSTR("\r press ESC to abort, power off in %ds (press key to skip timer) "), i); + key = waitAndReadKey(1000); + } + if (key == ESCAPE) { + return true; + } + setTimer(10, reg); + reg.reg1.field.af = 1; // alarm flag remains unchanged + reg.reg1.field.tf = 0; // timer flag clear + reg.reg1.field.tie = 1; // enable timer interrupt + rtc8563.writeByteAndBuffer(0x01, ®.reg1.byte, 1); + printf_P(PSTR("\n power off now ...")); + DDRC |= ( 1<< PC7); + PORTC |= (1 << PC7); + _delay_ms(5); + DDRC &= ~( 1<< PC7); + PORTC &= ~(1 << PC7); + waitAndReadKey(5000); + printf_P(PSTR("power off fails, I am still alive :-) ... proceed")); + } + return true; +} + + +bool Rtc8563::setClock (int8_t weekday, uint16_t year, int8_t month, int8_t day, int8_t hour, int8_t min, int8_t sec) { + Clock_t clock; + clock.month.field.century = (year < 2000 || year > 2100) ? 1: 0; + uint8_t y = year % 100; clock.year.field.bcdL = y % 10; clock.year.field.bcdH = y / 10; + + while (weekday < 0) { weekday += 7; } + while (weekday > 6) { weekday -= 7; } + clock.weekday.field.bcdL = weekday; + + while (month < 1) { month += 12; } + while (month > 12) { month -= 12; } + clock.month.field.bcdL = month % 10; clock.month.field.bcdH = month / 10; + + while (day < 1) { day += 31; } + while (day > 31) { day -= 31; } + clock.day.field.bcdL = day % 10; clock.day.field.bcdH = day / 10; + + while (hour < 0) { hour += 24; } + while (hour > 23) { hour -= 24; } + clock.hour.field.bcdL = hour % 10; clock.hour.field.bcdH = hour / 10; + + while (min < 0) { min += 60; } + while (min > 59) { min -= 60; } + clock.min.field.bcdL = min % 10; clock.min.field.bcdH = min / 10; + + while (sec < 0) { sec += 60; } + while (sec > 59) { sec -= 60; } + clock.sec.field.bcdL = sec % 10; clock.sec.field.bcdH = sec / 10; + + printf_P(PSTR("\n %p %p %p %p %p %p %p -> write: "), &clock.sec.byte, &clock.min.byte, &clock.hour.byte, &clock.day.byte, &clock.weekday.byte, &clock.month.byte, &clock.year.byte ); + for (uint8_t i = 0; i < sizeof(clock); i++) { + printf_P(PSTR(" %02x"), ((uint8_t *)(void *)&clock)[i]); + } + return rtc8563.writeByteAndBuffer(0x02, (uint8_t *)(void *)&clock, sizeof(clock)); +} + +#endif + diff --git a/software/deye-sun-12k/src/units/rtc8563.hpp b/software/deye-sun-12k/src/units/rtc8563.hpp new file mode 100644 index 0000000..ca79713 --- /dev/null +++ b/software/deye-sun-12k/src/units/rtc8563.hpp @@ -0,0 +1,70 @@ +#ifndef RTC8563_HPP +#define RTC8563_HPP + +#include +#include "../main.hpp" +#include "../adafruit/bme280.h" +#include "../adafruit/ens160.h" +#include "../i2cmaster.hpp" +#include "../i2cslave.hpp" + + +class Rtc8563 : public TestUnit { + public: + typedef enum { NORMAL } Rtc8563Mode_t; + + private: + I2cMaster rtc8563; + typedef enum { FDCLOCKOUT_32768HZ = 0, FDCLOCKOUT_1024HZ = 1, FDCLOCKOUT_32HZ = 2, FDCLOCKOUT_1HZ = 3 } FDCLOCKOUT_t; + typedef enum { FDTIMER_4096HZ = 0, FDTIMER_64HZ = 1, FDTIMER_1HZ = 2, FDTIMER_1D60HZ = 3 } FDTIMER_t; + typedef struct { + union { uint8_t byte; struct { uint8_t bcdL:4; uint8_t bcdH:3; uint8_t voltageLow:1; } field; } sec; + union { uint8_t byte; struct { uint8_t bcdL:4; uint8_t bcdH:3; uint8_t notUsed:1; } field; } min; + union { uint8_t byte; struct { uint8_t bcdL:4; uint8_t bcdH:2; uint8_t notUsed:2; } field; } hour; + union { uint8_t byte; struct { uint8_t bcdL:4; uint8_t bcdH:2; uint8_t notUsed:2; } field; } day; + union { uint8_t byte; struct { uint8_t bcdL:3; } field; uint8_t notUsed:5; } weekday; + union { uint8_t byte; struct { uint8_t bcdL:4; uint8_t bcdH:1; uint8_t notUsed:2; uint8_t century:1; } field; } month; + union { uint8_t byte; struct { uint8_t bcdL:4; uint8_t bcdH:4; } field; } year; + } Clock_t; // identical to 8563 register 2..8 + + typedef struct { + union { uint8_t byte; struct { uint8_t bcdL:4; uint8_t bcdH:3; uint8_t enable:1; } field; } min; + union { uint8_t byte; struct { uint8_t bcdL:4; uint8_t bcdH:2; uint8_t notUsed:1; uint8_t enable:1; } field; } hour; + union { uint8_t byte; struct { uint8_t bcdL:4; uint8_t bcdH:2; uint8_t notUsed:1; uint8_t enable:1; } field; } day; + union { uint8_t byte; struct { uint8_t bcdL:3; } field; uint8_t notUsed:4; uint8_t enable_W:1; } weekday; + } Alarm_t; + + typedef struct { + union { uint8_t byte; struct { uint8_t notUsed210:3; uint8_t testC:1; uint8_t notUsed4:1; uint8_t stop:1; uint8_t notUsed6:1; uint8_t test1:1; } field; } reg0; + union { uint8_t byte; struct { uint8_t tie:1; uint8_t aie:1; uint8_t tf:1; uint8_t af:1; uint8_t ti_tp:1; uint8_t notUsed:3; } field; } reg1; + Clock_t clock; + Alarm_t alarm; + union { uint8_t byte; struct { FDCLOCKOUT_t fd:2; uint8_t notUsed65432:5; uint8_t enable:1; } field; } clockoutControl; + union { uint8_t byte; struct { FDTIMER_t fd:2; uint8_t notUsed65432:5; uint8_t enable:1; } field; } timerControl; + uint8_t timer; + } Rtc8563Reg_t; + + + public: + bool enabled; + + public: + Rtc8563 (Rtc8563Mode_t mode) { enabled = false; this->mode = mode; } + void tick1ms () { rtc8563.tick1ms(); } + virtual void init (); + virtual void cleanup (); + virtual int8_t run (uint8_t subtest); + virtual PGM_P getName (); + void handleTwiIrq (); + + private: + Rtc8563Mode_t mode; + uint8_t bcd2bin (uint8_t value); + int8_t runModeNormal (uint8_t subtest); + int8_t runModeBattery (uint8_t subtest); + bool setTimer (uint8_t seconds, Rtc8563Reg_t ®); + bool powerOnOff (uint8_t delayOffSeconds, Rtc8563Reg_t ®); + bool setClock (int8_t weekday, uint16_t year, int8_t month, int8_t day, int8_t hour, int8_t min, int8_t sec); +}; + +#endif \ No newline at end of file diff --git a/software/deye-sun-12k/src/units/seg7.cpp b/software/deye-sun-12k/src/units/seg7.cpp new file mode 100644 index 0000000..f522456 --- /dev/null +++ b/software/deye-sun-12k/src/units/seg7.cpp @@ -0,0 +1,283 @@ +#include +#include + +#include "seg7.hpp" +#include "../main.hpp" + +#if defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__) + + // Nano-644 / Nano 1284 + // --------------------------------------------------------------- + + // Nano-X-Base V1a V2a + // --------------------------------- + // Anode Segment A ....... PB0 PB0 + // Anode Segment B ....... PB1 PB1 + // Anode Segment C ....... PB2 PB2 + // Anode Segment D ....... PB3 PB3 + // Anode Segment E ....... PB4 PB4 + // Anode Segment F ....... PB5 PB5 + // Anode Segment G ....... PB6 PB6 + // Anode DP .............. PB7 PB7 + // --------------------------------- + // Cathode Char 1 ........ PA0 PD4 + // Cathode Char 2 ........ PA1 PD5 + // Cathode Char 3 ........ PA2 PD6 + // Cathode Char 4 ........ PA3 PD7 + // --------------------------------- + // nOE (Output Enable) ... PD5 PA1 + // Anode L1:2 ............ PD6 PA2 + // Anode L3 .............. PD7 PA3 + +void Seg7::init () { + setAnodes(0); + setCathodes(0); + DDRB = 0xff; + switch (hardwareVersion) { + case 1: { + DDRA |= (1 << PA3) | (1 << PA2) | (1 << PA1) | (1 << PA0); + DDRD |= (1 << PD7) | (1 << PD6) | (1 << PD5); + break; + } + case 2: { + DDRA |= (1 << PA3) | (1 << PA2) | (1 << PA1); + DDRD |= (1 << PD7) | (1 << PD6) | (1 << PD5) | (1 << PD4); + } + break; + } + } + + void Seg7::cleanup () { + setAnodes(0); + setCathodes(0); + DDRB = 0x00; + switch (hardwareVersion) { + case 1: { + DDRA &= ~((1 << PA3) | (1 << PA2) | (1 << PA1) | (1 << PA0)); + DDRD &= ~((1 << PD7) | (1 << PD6) | (1 << PD5)); + break; + } + case 2: { + DDRA &= ~((1 << PA3) | (1 << PA2) | (1 << PA1)); + DDRD &= ~((1 << PD7) | (1 << PD6) | (1 << PD5) | (1 << PD4)); + } + break; + } + } + + void Seg7::setAnodes (uint16_t a) { + if (a & 0x0001) PORTB |= (1 << PB0); else PORTB &= ~(1 << PB0); // Anode Char A + if (a & 0x0002) PORTB |= (1 << PB1); else PORTB &= ~(1 << PB1); // Anode Char B + if (a & 0x0004) PORTB |= (1 << PB2); else PORTB &= ~(1 << PB2); // Anode Char C + if (a & 0x0008) PORTB |= (1 << PB3); else PORTB &= ~(1 << PB3); // Anode Char D + if (a & 0x0010) PORTB |= (1 << PB4); else PORTB &= ~(1 << PB4); // Anode Char E + if (a & 0x0020) PORTB |= (1 << PB5); else PORTB &= ~(1 << PB5); // Anode Char F + if (a & 0x0040) PORTB |= (1 << PB6); else PORTB &= ~(1 << PB6); // Anode Char G + if (a & 0x0080) PORTB |= (1 << PB7); else PORTB &= ~(1 << PB7); // Anode Char DP + switch (hardwareVersion) { + case 1: { + if (a & 0x0100) PORTD |= (1 << PD6); else PORTD &= ~(1 << PD6); // Anode L1/L2 + if (a & 0x0200) PORTD |= (1 << PD7); else PORTD &= ~(1 << PD7); // Anode L3 + break; + } + case 2: { + if (a & 0x0100) PORTA |= (1 << PA2); else PORTA &= ~(1 << PA2); // Anode L1/L2 + if (a & 0x0200) PORTA |= (1 << PA3); else PORTA &= ~(1 << PA3); // Anode L3 + break; + } + } + } + + void Seg7::setCathodes (uint8_t c) { + switch (hardwareVersion) { + case 1: { + if (c & 0x01) PORTA |= (1 << PA0); else PORTA &= ~(1 << PA0); // Chathode Char 1 (most left) + if (c & 0x02) PORTA |= (1 << PA1); else PORTA &= ~(1 << PA1); // Chathode Char 2 + if (c & 0x04) PORTA |= (1 << PA2); else PORTA &= ~(1 << PA2); // Chathode Char 3 + if (c & 0x08) PORTA |= (1 << PA3); else PORTA &= ~(1 << PA3); // Chathode Char 4 (most right) + break; + } + case 2: { + if (c & 0x01) PORTD |= (1 << PD4); else PORTD &= ~(1 << PD4); // Chathode Char 1 (most left) + if (c & 0x02) PORTD |= (1 << PD5); else PORTD &= ~(1 << PD5); // Chathode Char 2 + if (c & 0x04) PORTD |= (1 << PD6); else PORTD &= ~(1 << PD6); // Chathode Char 3 + if (c & 0x08) PORTD |= (1 << PD7); else PORTD &= ~(1 << PD7); // Chathode Char 4 (most right) + break; + } + } + } + + void Seg7::setOE (bool enabled) { + switch (hardwareVersion) { + case 1: if (enabled) PORTD &= ~(1 << PD5); else PORTD |= (1 << PD5); break; + case 2: if (enabled) PORTA &= ~(1 << PA1); else PORTA |= (1 << PA1); break; + } + } + +#endif + +#ifdef __AVR_ATmega328P__ + + // Arduino-Nano-5V + // --------------------------------------------------------------- + + // Nano-X-Base V1a V2a + // --------------------------------- + // Anode Segment A ....... PD4 PD4 + // Anode Segment B ....... PB0 PB0 + // Anode Segment C ....... PD7 PD7 + // Anode Segment D ....... PD6 PD6 + // Anode Segment E ....... PB2 PB2 + // Anode Segment F ....... PB3 PB3 + // Anode Segment G ....... PB4 PB4 + // Anode DP .............. PB5 PB5 + // --------------------------------- + // Cathode Char 1 ........ PC0 PD5 + // Cathode Char 2 ........ PC1 PB1 + // Cathode Char 3 ........ PC2 PD3 + // Cathode Char 4 ........ PC3 PD2 + // --------------------------------- + // nOE (Output Enable) ... PB1 PC1 + // Anode L1:2 ............ PD3 PC2 + // Anode L3 .............. PD2 PC3 + + + void Seg7::init () { + enabled = 1; + setAnodes(0); + setCathodes(0); + switch (hardwareVersion) { + case 1: { + DDRB |= (1 << PB5) | (1 << PB4) | (1 << PB3) | (1 << PB2) | (1 << PB1) | (1 << PB0); + DDRC |= (1 << PC3) | (1 << PC2) | (1 << PC1) | (1 << PC0); + DDRD |= (1 << PD7) | (1 << PD6) | (1 << PD4) | (1 << PD3) | (1 << PD2); + break; + } + case 2: { + DDRB |= (1 << PB5) | (1 << PB4) | (1 << PB3) | (1 << PB2) | (1 << PB1) | (1 << PB0); + DDRC |= (1 << PC3) | (1 << PC2) | (1 << PC1); + DDRD |= (1 << PD7) | (1 << PD6) | (1 << PD5) | (1 << PD4) | (1 << PD3) | (1 << PD2); + break; + } + } + + } + + void Seg7::cleanup () { + enabled = 0; + setAnodes(0); + setCathodes(0); + switch (hardwareVersion) { + case 1: { + DDRB &= ~((1 << PB5) | (1 << PB4) | (1 << PB3) | (1 << PB2) | (1 << PB1) | (1 << PB0)); + DDRC &= ~((1 << PC3) | (1 << PC2) | (1 << PC1) | (1 << PC0)); + DDRD &= ~((1 << PD7) | (1 << PD6) | (1 << PD4) | (1 << PD3) | (1 << PD2)); + break; + } + case 2: { + DDRB &= ~((1 << PB5) | (1 << PB4) | (1 << PB3) | (1 << PB2) | (1 << PB1) | (1 << PB0) ); + DDRC &= ~((1 << PC3) | (1 << PC2) | (1 << PC1)); + DDRD &= ~((1 << PD7) | (1 << PD6) | (1 << PD5) | (1 << PD4) | (1 << PD3) | (1 << PD2)); + break; + } + } + + } + + void Seg7::setAnodes (uint16_t a) { + if (a & 0x0001) PORTD |= (1 << PD4); else PORTD &= ~(1 << PD4); // Anode Char A + if (a & 0x0002) PORTB |= (1 << PB0); else PORTB &= ~(1 << PB0); // Anode Char B + if (a & 0x0004) PORTD |= (1 << PD7); else PORTD &= ~(1 << PD7); // Anode Char C + if (a & 0x0008) PORTD |= (1 << PD6); else PORTD &= ~(1 << PD6); // Anode Char D + if (a & 0x0010) PORTB |= (1 << PB2); else PORTB &= ~(1 << PB2); // Anode Char E + if (a & 0x0020) PORTB |= (1 << PB3); else PORTB &= ~(1 << PB3); // Anode Char F + if (a & 0x0040) PORTB |= (1 << PB4); else PORTB &= ~(1 << PB4); // Anode Char G + if (a & 0x0080) PORTB |= (1 << PB5); else PORTB &= ~(1 << PB5); // Anode Char DP + switch (hardwareVersion) { + case 1: { + if (a & 0x0100) PORTD |= (1 << PD3); else PORTD &= ~(1 << PD3); // Anode L1/L2 + if (a & 0x0200) PORTD |= (1 << PD2); else PORTD &= ~(1 << PD2); // Anode L3 + break; + } + case 2: { + if (a & 0x0100) PORTC |= (1 << PC2); else PORTC &= ~(1 << PC2); // Anode L1/L2 + if (a & 0x0200) PORTC |= (1 << PC3); else PORTC &= ~(1 << PC3); // Anode L3 + break; + } + } + } + + void Seg7::setCathodes (uint8_t c) { + switch (hardwareVersion) { + case 1: { + if (c & 0x01) PORTC |= (1 << PC0); else PORTC &= ~(1 << PC0); // Chathode Char 1 (most left) + if (c & 0x02) PORTC |= (1 << PC1); else PORTC &= ~(1 << PC1); // Chathode Char 2 + if (c & 0x04) PORTC |= (1 << PC2); else PORTC &= ~(1 << PC2); // Chathode Char 3 + if (c & 0x08) PORTC |= (1 << PC3); else PORTC &= ~(1 << PC3); // Chathode Char 4 (most right) + break; + } + case 2: { + if (c & 0x01) PORTD |= (1 << PD5); else PORTD &= ~(1 << PD5); // Chathode Char 1 (most left) + if (c & 0x02) PORTB |= (1 << PB1); else PORTB &= ~(1 << PB1); // Chathode Char 2 + if (c & 0x04) PORTD |= (1 << PD3); else PORTD &= ~(1 << PD3); // Chathode Char 3 + if (c & 0x08) PORTD |= (1 << PD2); else PORTD &= ~(1 << PD2); // Chathode Char 4 (most right) + break; + } + } + } + + void Seg7::setOE (bool enabled) { + switch (hardwareVersion) { + case 1: if (enabled) PORTB &= ~(1 << PB1); else PORTB |= (1 << PB1); break; + case 2: if (enabled) PORTC &= ~(1 << PC1); else PORTC |= (1 << PC1); break; + } + } + +#endif + +const char *segName[] = { "A", "B", "C", "D", "E", "F", "G", "DP" }; + + +int8_t Seg7::run (uint8_t subtest) { + if (subtest == 0) { + setCathodes(0x0f); // all segment cathodes conected to GND + setAnodes(0x3ff); // all segments ON + setOE(true); + printf_P(PSTR("ON")); + wait(2000); + setAnodes(0); + return 0; + + } else if (subtest == 1) { + printf_P(PSTR("OFF")); + wait(1000); + return 0; + + } else if (subtest == 2) { + setAnodes(0x100); // L1/L2 ON + printf_P(PSTR("L1/L2 ON")); + wait(1000); + setAnodes(0); + return 0; + + } else if (subtest == 3) { + setAnodes(0x200); // L3 ON + printf_P(PSTR("L1/L2 ON")); + wait(1000); + setAnodes(0); + return 0; + + } else if (subtest < (4 + 4 * 8)) { + uint8_t chIndex = (subtest - 4) / 8; + uint8_t segIndex = (subtest - 4) % 8; + setCathodes(1 << chIndex); + setAnodes(1 << segIndex); + printf_P(PSTR("Char %d - %s -> %02x"), chIndex, segName[segIndex], (1 << segIndex)); + wait(400); + return 0; + } + + return -1; +} + + diff --git a/software/deye-sun-12k/src/units/seg7.hpp b/software/deye-sun-12k/src/units/seg7.hpp new file mode 100644 index 0000000..0e71fde --- /dev/null +++ b/software/deye-sun-12k/src/units/seg7.hpp @@ -0,0 +1,25 @@ +#ifndef SEG7_HPP +#define SEG7_HPP + +#include +#include "../main.hpp" +#include + +class Seg7 : public TestUnit { + public: + bool enabled; + + public: + Seg7 () { enabled = false; } + virtual void init (); + virtual void cleanup (); + virtual int8_t run (uint8_t subtest); + virtual PGM_P getName () { return PSTR("Seg7"); } + + private: + void setAnodes (uint16_t); + void setCathodes (uint8_t mask); + void setOE (bool enabled); +}; + +#endif \ No newline at end of file diff --git a/software/deye-sun-12k/src/units/switch.cpp b/software/deye-sun-12k/src/units/switch.cpp new file mode 100644 index 0000000..4ce9456 --- /dev/null +++ b/software/deye-sun-12k/src/units/switch.cpp @@ -0,0 +1,100 @@ +#include +#include +#include + +#include "switch.hpp" +#include "../main.hpp" + +#if defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__) + + // Nano-644 + // --------------------------------------------------------------- + // PA0 ..... SW1 + // PA1 ..... SW2 + // PA2 ..... SW3 + // PA3 ..... SW4 + + void Switch::init () { + DDRA &= ~((1 << PORTA3) | (1 << PORTA2) | (1 << PORTA1) | (1 << PORTA0)); + PORTA |= (1 << PORTA3) | (1 << PORTA2) | (1 << PORTA1) | (1 << PORTA0); + } + + void Switch::cleanup () { + PORTA &= ~((1 << PORTA3) | (1 << PORTA2) | (1 << PORTA1) | (1 << PORTA0)); + DDRA &= ~((1 << PORTA3) | (1 << PORTA2) | (1 << PORTA1) | (1 << PORTA0)); + } + + bool Switch::isPressed (SWITCH sw) { + switch (sw) { + case SW1: return (PINA & ( 1 << PA0)) == 0; + case SW2: return (PINA & ( 1 << PA1)) == 0; + case SW3: return (PINA & ( 1 << PA2)) == 0; + case SW4: return (PINA & ( 1 << PA3)) == 0; + default: return false; + } + } + +#endif + +#ifdef __AVR_ATmega328P__ + + // Arduino-Nano-5V + // --------------------------------------------------------------- + // PC0 ..... SW1 + // PC1 ..... SW2 + // PC2 ..... SW3 + // PC3 ..... SW4 + + void Switch::init () { + DDRC &= ~((1 << PORTC3) | (1 << PORTC2) | (1 << PORTC1) | (1 << PORTC0)); + PORTC |= (1 << PORTC3) | (1 << PORTC2) | (1 << PORTC1) | (1 << PORTC0); + } + + void Switch::cleanup () { + PORTC &= ~((1 << PORTC3) | (1 << PORTC2) | (1 << PORTC1) | (1 << PORTC0)); + DDRC &= ~((1 << PORTC3) | (1 << PORTC2) | (1 << PORTC1) | (1 << PORTC0)); + } + + bool Switch::isPressed (SWITCH sw) { + switch (sw) { + case SW1: return (PINC & ( 1 << PC0)) == 0; + case SW2: return (PINC & ( 1 << PC1)) == 0; + case SW3: return (PINC & ( 1 << PC2)) == 0; + case SW4: return (PINC & ( 1 << PC3)) == 0; + default: return false; + } + } + +#endif + +int8_t Switch::run (uint8_t subtest) { + if (subtest < 16) { + SWITCH sw = (SWITCH)(subtest / 4); + switch (subtest % 4) { + case 1: { + if (!isPressed(sw)) { + printf_P(PSTR("Press SW%d"), sw + 1); + while (!isPressed(sw) && wait(0) == EOF) {} + wait(10); + } + return 0; + } + + case 0: case 2: { + if (isPressed(sw)) { + printf_P(PSTR("Release SW%d "), sw + 1); + while (isPressed(sw) && wait(0) == EOF) {} + wait(10); + } + return 0; + } + + case 3: { + return 0; + } + } + + } + + return -1; +} diff --git a/software/deye-sun-12k/src/units/switch.hpp b/software/deye-sun-12k/src/units/switch.hpp new file mode 100644 index 0000000..03bf0b2 --- /dev/null +++ b/software/deye-sun-12k/src/units/switch.hpp @@ -0,0 +1,20 @@ +#ifndef SWITCH_HPP +#define SWITCH_HPP + +#include +#include "../main.hpp" +#include + +class Switch : public TestUnit { + typedef enum { SW1 = 0, SW2 = 1, SW3 = 2, SW4 = 3 } SWITCH; + + public: + Switch () {}; + virtual void init (); + virtual void cleanup (); + virtual int8_t run (uint8_t subtest); + virtual PGM_P getName () { return PSTR("Switch"); } + bool isPressed (SWITCH sw); +}; + +#endif \ No newline at end of file diff --git a/software/deye-sun-12k/src/units/uart1.cpp b/software/deye-sun-12k/src/units/uart1.cpp new file mode 100644 index 0000000..57e0bfb --- /dev/null +++ b/software/deye-sun-12k/src/units/uart1.cpp @@ -0,0 +1,65 @@ +#include +#include +#include + +#include "uart1.hpp" +#include "../main.hpp" + +#ifdef __AVR_ATmega328P__ + void Uart1::init () {} + void Uart1::cleanup () {} + int8_t Uart1::run (uint8_t subtest) { return -1; } + void Uart1::handleRxByte (uint8_t b) {} +#endif + +#if defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__) + +int uart1_putchar(char c, FILE *stream) { + if (c == '\n') { + uart1_putchar('\r', stream); + } + loop_until_bit_is_set(UCSR1A, UDRE1); + UDR1 = c; + return 0; +} + +static FILE mystderr = { 0, 0, _FDEV_SETUP_WRITE , 0, 0, uart1_putchar, NULL, 0 }; + +void Uart1::init () { + PORTD |= (1 << PD2); // enable RxD1 pullup + UCSR1A = (1 << U2X1); + UCSR1B = (1 << RXCIE1) | (1 << RXEN1) | (1 < send text via UART1 now...")); + fprintf_P(stderr, PSTR("Hello UART1, ECHO-Modus active\n")); + } while (wait(5000) == EOF); + return 0; + } + + return -1; +} + +void Uart1::handleRxByte (uint8_t b) { + uart1_putchar(b, stderr); +} + +#endif \ No newline at end of file diff --git a/software/deye-sun-12k/src/units/uart1.hpp b/software/deye-sun-12k/src/units/uart1.hpp new file mode 100644 index 0000000..40437e1 --- /dev/null +++ b/software/deye-sun-12k/src/units/uart1.hpp @@ -0,0 +1,21 @@ +#ifndef UART1_HPP +#define UART1_HPP + +#include +#include "../main.hpp" +#include + +class Uart1 : public TestUnit { + public: + uint8_t enabled; + + public: + Uart1 () { enabled = 0; } + virtual void init (); + virtual void cleanup (); + virtual int8_t run (uint8_t subtest); + virtual PGM_P getName () { return PSTR("Uart1"); } + void handleRxByte (uint8_t); +}; + +#endif \ No newline at end of file -- 2.39.5