From: Manfred Steiner Date: Sat, 5 Apr 2025 08:39:00 +0000 (+0200) Subject: Software für Wechselrichter Deye-SUN-12K X-Git-Url: https://git.htl-mechatronik.at/public/?a=commitdiff_plain;h=e5c4924d6d38a91c8d2f6a1f6105e22088b10e03;p=nano-x-base.git Software für Wechselrichter Deye-SUN-12K --- 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 0000000..06e910a Binary files /dev/null and b/software/deye-sun-12k/nano-1284/release/v2024-11-08_181806/nano-x-base_test-software_nano-m1284p_12mhz.bin differ 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 0000000..31affaa Binary files /dev/null and b/software/deye-sun-12k/nano-1284/release/v2024-11-08_181806/nano-x-base_test-software_nano-m1284p_12mhz.elf differ 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 0000000..0c7d86e Binary files /dev/null and b/software/deye-sun-12k/nano-644/release/v2024-11-08_181803/nano-x-base_test-software_nano-m644p_12mhz.bin differ 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 0000000..7d33607 Binary files /dev/null and b/software/deye-sun-12k/nano-644/release/v2024-11-08_181803/nano-x-base_test-software_nano-m644p_12mhz.elf differ 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