--- /dev/null
+target remote :1234
+layout split
+stepi
+quit
+target remote :1234
+layout split
+stepi
+b *main+9
+quit
--- /dev/null
+.depend
+**/build
+**/dist
+**/sim
--- /dev/null
+{
+ "configurations": [
+ {
+ "name": "Linux AVR",
+ "includePath": [
+ "/usr/lib/avr/include/**",
+ "/usr/lib/gcc/avr/**"
+ ],
+ "defines": [],
+ "compilerPath": "/usr/bin/avr-gcc",
+ "compilerArgs": [ "-mmcu=atmega644p", "-DF_CPU=12000000", "-Os" ],
+ "cStandard": "gnu11",
+ "cppStandard": "gnu++11",
+ "intelliSenseMode": "linux-gcc-x64"
+ }
+ ],
+ "version": 4
+}
--- /dev/null
+{
+ // 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"
+ }
+ ]
+}
--- /dev/null
+{
+ "[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"
+ ]
+}
--- /dev/null
+{
+ // 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
--- /dev/null
+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.
--- /dev/null
+.PHONY: all release 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
+ @printf "\n\e[1;36m$(SEP)\n Arduino Nano\n$(SEP)\e[m\n"
+ -@make -C nano-328
+
+release:
+ -@make -C nano-644 release
+ -@make -C nano-1284 release
+ -@make -C nano-328 release
+
+clean:
+ @make -C nano-644 clean
+ @make -C nano-1284 clean
+ @make -C nano-328 clean
+
--- /dev/null
+# Test-Software for Nano-X-Base
+
+This software supports:
+* Arduino Nano (ATmega328P @16MHz)
+* Nano-644 (ATmega644P @12MHz)
+* Nano-1284 (ATmega1284P @12MHz)
+
+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)
--- /dev/null
+#!/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
+
--- /dev/null
+.PHONY: all info flash flash0 flash1 flash2 picocom picocom0 picocom1 picocom2 release clean
+$(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)
+
+NAME=test-software_nano-1284p_12mhz
+SRC= $(wildcard src/*.c src/*.cpp src/*/*.c src/*/*.cpp)
+HDR= $(wildcard src/*.h src/*.hpp src/*/*.h src/*/*.hpp)
+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)
+
+DEVICE=atmega1284p
+AVRDUDE_DEVICE=m1284p
+
+CC= avr-g++
+CFLAGS= -Wall -mmcu=$(DEVICE) -Os -DF_CPU=12000000 -c
+LFLAGS= -Wall -mmcu=$(DEVICE) -Os -DF_CPU=12000000 -Wl,-u,vfprintf -lprintf_flt -lm
+
+CFLAGS_SIM= -Wall -mmcu=$(DEVICE) -Og -DF_CPU=12000000 -g -c -c
+LFLAGS_SIM= -Wall -mmcu=$(DEVICE) -Og -DF_CPU=12000000 -g -Wl,-u,vfprintf -lprintf_flt -lm
+
+# ------------------------------------------------
+
+all: dist/$(NAME).elf dist/$(NAME).s dist/$(NAME).hex sim/$(NAME).elf sim/$(NAME).s info
+
+isp-fuse-$(AVRDUDE_DEVICE):
+ avrdude -c usbasp -p $(AVRDUDE_DEVICE) -U lfuse:w:0xFF:m -U hfuse:w:0xD9:m -U efuse:w:0xFF:m -U lock:w:0xFF:m
+
+info:
+ @avr-size --mcu=$(DEVICE) --format=avr dist/$(NAME).elf
+
+.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
+
+-include .depend
+
+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) $< $@
+
+sim/$(NAME).elf: .depend $(OBJ_SIM)
+ $(CC) $(LFLAGS_SIM) -o $@ $(OBJ_SIM)
+
+# ensure that __DATE__ and __TIME__ macros are up to date
+build/main.o: src/main.cpp $(SRC) $(HDR)
+ @mkdir -p $(dir $@)
+ $(CC) $(CFLAGS) -o $@ $<
+
+build/%.o: src/%.c
+ @mkdir -p $(dir $@)
+ $(CC) $(CFLAGS) -o $@ $<
+
+build/%.o: src/%.cpp
+ @mkdir -p $(dir $@)
+ $(CC) $(CFLAGS) -o $@ $<
+
+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 $< > $@
+
+simuc: sim/$(NAME).elf
+ simuc --board nano-1284 $<
+
+gdb: sim/$(NAME).elf
+ avr-gdb $<
+
+
+flash: dist/$(NAME).elf all
+ avrdude -c arduino -P /dev/ttyUSB0 -p $(AVRDUDE_DEVICE) -e -U flash:w:$<
+
+flash0: dist/$(NAME).elf all
+ avrdude -c arduino -P /dev/ttyUSB0 -p $(AVRDUDE_DEVICE) -e -U flash:w:$<
+
+flash1: dist/$(NAME).elf all
+ avrdude -c arduino -P /dev/ttyUSB1 -p $(AVRDUDE_DEVICE) -e -U flash:w:$<
+
+flash2: dist/$(NAME).elf all
+ avrdude -c arduino -P /dev/ttyUSB2 -p $(AVRDUDE_DEVICE) -e -U flash:w:$<
+
+
+picocom:
+ # picocom sends CR for ENTER -> convert cr (\r) to lf (\n)
+ picocom -b 115200 --omap crlf --raise-dtr /dev/ttyUSB0
+
+picocom0:
+ picocom -b 115200 --omap crlf --raise-dtr /dev/ttyUSB0
+
+picocom1:
+ picocom -b 115200 --omap crlf --raise-dtr /dev/ttyUSB1
+
+picocom2:
+ picocom -b 115200 --omap crlf --raise-dtr /dev/ttyUSB2
+
+
+isp-1284p:
+ avrdude -c usbasp -p $(AVRDUDE_DEVICE)
+
+isp-flash-1284p: dist/$(NAME).elf all
+ avrdude -c usbasp -p $(AVRDUDE_DEVICE) -e -U flash:w:$<
+
+flash-1284p: dist/$(NAME).elf all
+ avrdude -c arduino -p $(AVRDUDE_DEVICE) -P /dev/ttyUSB0 -b 115200 -e -U flash:w:$<
+
+release: dist/$(NAME).elf sim/$(NAME).elf
+ ../create-release release $(word 1, $^) release/sim $(word 2, $^)
+
+clean:
+ @rm -r dist
+ @rm -r build
+ @rm -r sim
+ @find . -type f -name ".depend" -exec rm {} \;
+ @echo "clean done"
--- /dev/null
+../src
\ No newline at end of file
--- /dev/null
+.PHONY: all info flash flash0 flash1 flash2 picocom picocom0 picocom1 picocom2 release clean
+$(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)
+
+NAME=test-software_nano-328p_16mhz
+SRC= $(wildcard src/*.c src/*.cpp src/*/*.c src/*/*.cpp)
+HDR= $(wildcard src/*.h src/*.hpp src/*/*.h src/*/*.hpp)
+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)
+
+DEVICE=atmega328p
+AVRDUDE_DEVICE=m328p
+
+CC= avr-g++
+CFLAGS= -Wall -mmcu=$(DEVICE) -Os -DF_CPU=16000000 -c
+LFLAGS= -Wall -mmcu=$(DEVICE) -Os -DF_CPU=16000000 -Wl,-u,vfprintf -lprintf_flt -lm
+
+CFLAGS_SIM= -Wall -mmcu=$(DEVICE) -Og -DF_CPU=16000000 -g -c -c
+LFLAGS_SIM= -Wall -mmcu=$(DEVICE) -Og -DF_CPU=16000000 -g -Wl,-u,vfprintf -lprintf_flt -lm
+
+
+all: dist/$(NAME).elf dist/$(NAME).s dist/$(NAME).hex sim/$(NAME).elf sim/$(NAME).s info
+
+isp-fuse-$(AVRDUDE_DEVICE):
+ avrdude -c usbasp -p $(AVRDUDE_DEVICE) -U lfuse:w:0xFF:m -U hfuse:w:0xD9:m -U efuse:w:0xFF:m -U lock:w:0xFF:m
+
+# ------------------------------------------------
+
+info:
+ @avr-size --mcu=$(DEVICE) --format=avr dist/$(NAME).elf
+
+.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
+
+-include .depend
+
+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) $< $@
+
+sim/$(NAME).elf: .depend $(OBJ_SIM)
+ $(CC) $(LFLAGS_SIM) -o $@ $(OBJ_SIM)
+
+# ensure that __DATE__ and __TIME__ macros are up to date
+build/main.o: src/main.cpp $(SRC) $(HDR)
+ @mkdir -p $(dir $@)
+ $(CC) $(CFLAGS) -o $@ $<
+
+build/%.o: src/%.c
+ @mkdir -p $(dir $@)
+ $(CC) $(CFLAGS) -o $@ $<
+
+build/%.o: src/%.cpp
+ @mkdir -p $(dir $@)
+ $(CC) $(CFLAGS) -o $@ $<
+
+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 $< > $@
+
+simuc: sim/$(NAME).elf
+ simuc --board nano-1284 $<
+
+gdb: sim/$(NAME).elf
+ avr-gdb $<
+
+
+flash: dist/$(NAME).elf all
+ avrdude -c arduino -P /dev/ttyUSB0 -p $(AVRDUDE_DEVICE) -e -U flash:w:$<
+
+flash0: dist/$(NAME).elf all
+ avrdude -c arduino -P /dev/ttyUSB0 -p $(AVRDUDE_DEVICE) -e -U flash:w:$<
+
+flash1: dist/$(NAME).elf all
+ avrdude -c arduino -P /dev/ttyUSB1 -p $(AVRDUDE_DEVICE) -e -U flash:w:$<
+
+flash2: dist/$(NAME).elf all
+ avrdude -c arduino -P /dev/ttyUSB2 -p $(AVRDUDE_DEVICE) -e -U flash:w:$<
+
+
+picocom:
+ # picocom sends CR for ENTER -> convert cr (\r) to lf (\n)
+ picocom -b 115200 --omap crlf --raise-dtr /dev/ttyUSB0
+
+picocom0:
+ picocom -b 115200 --omap crlf --raise-dtr /dev/ttyUSB0
+
+picocom1:
+ picocom -b 115200 --omap crlf --raise-dtr /dev/ttyUSB1
+
+picocom2:
+ picocom -b 115200 --omap crlf --raise-dtr /dev/ttyUSB2
+
+
+isp-1284p:
+ avrdude -c usbasp -p $(AVRDUDE_DEVICE)
+
+isp-flash-1284p: dist/$(NAME).elf all
+ avrdude -c usbasp -p $(AVRDUDE_DEVICE) -e -U flash:w:$<
+
+flash-1284p: dist/$(NAME).elf all
+ avrdude -c arduino -p $(AVRDUDE_DEVICE) -P /dev/ttyUSB0 -b 115200 -e -U flash:w:$<
+
+release: dist/$(NAME).elf sim/$(NAME).elf
+ ../create-release release $(word 1, $^) release/sim $(word 2, $^)
+
+clean:
+ @rm -r dist
+ @rm -r build
+ @rm -r sim
+ @find . -type f -name ".depend" -exec rm {} \;
+ @echo "clean done"
--- /dev/null
+../src
\ No newline at end of file
--- /dev/null
+.PHONY: all info flash flash0 flash1 flash2 picocom picocom0 picocom1 picocom2 release clean
+$(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)
+
+NAME=test-software_nano-644_12mhz
+SRC= $(wildcard src/*.c src/*.cpp src/*/*.c src/*/*.cpp)
+HDR= $(wildcard src/*.h src/*.hpp src/*/*.h src/*/*.hpp)
+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)
+
+DEVICE=atmega644p
+AVRDUDE_DEVICE=m644p
+
+CC= avr-g++
+CFLAGS= -Wall -mmcu=$(DEVICE) -Os -DF_CPU=12000000 -c
+LFLAGS= -Wall -mmcu=$(DEVICE) -Os -DF_CPU=12000000 -Wl,-u,vfprintf -lprintf_flt -lm
+
+CFLAGS_SIM= -Wall -mmcu=$(DEVICE) -Og -DF_CPU=12000000 -g -c -c
+LFLAGS_SIM= -Wall -mmcu=$(DEVICE) -Og -DF_CPU=12000000 -g -Wl,-u,vfprintf -lprintf_flt -lm
+
+# ------------------------------------------------
+
+all: dist/$(NAME).elf dist/$(NAME).s dist/$(NAME).hex sim/$(NAME).elf sim/$(NAME).s info
+
+isp-fuse-$(AVRDUDE_DEVICE):
+ avrdude -c usbasp -p $(AVRDUDE_DEVICE) -U lfuse:w:0xFF:m -U hfuse:w:0xD9:m -U efuse:w:0xFF:m -U lock:w:0xFF:m
+info:
+ @avr-size --mcu=$(DEVICE) --format=avr dist/$(NAME).elf
+
+.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
+
+-include .depend
+
+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) $< $@
+
+sim/$(NAME).elf: .depend $(OBJ_SIM)
+ $(CC) $(LFLAGS_SIM) -o $@ $(OBJ_SIM)
+
+# ensure that __DATE__ and __TIME__ macros are up to date
+build/main.o: src/main.cpp $(SRC) $(HDR)
+ @mkdir -p $(dir $@)
+ $(CC) $(CFLAGS) -o $@ $<
+
+build/%.o: src/%.c
+ @mkdir -p $(dir $@)
+ $(CC) $(CFLAGS) -o $@ $<
+
+build/%.o: src/%.cpp
+ @mkdir -p $(dir $@)
+ $(CC) $(CFLAGS) -o $@ $<
+
+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 $< > $@
+
+simuc: sim/$(NAME).elf
+ simuc --board nano-1284 $<
+
+gdb: sim/$(NAME).elf
+ avr-gdb $<
+
+
+flash: dist/$(NAME).elf all
+ avrdude -c arduino -P /dev/ttyUSB0 -p $(AVRDUDE_DEVICE) -e -U flash:w:$<
+
+flash0: dist/$(NAME).elf all
+ avrdude -c arduino -P /dev/ttyUSB0 -p $(AVRDUDE_DEVICE) -e -U flash:w:$<
+
+flash1: dist/$(NAME).elf all
+ avrdude -c arduino -P /dev/ttyUSB1 -p $(AVRDUDE_DEVICE) -e -U flash:w:$<
+
+flash2: dist/$(NAME).elf all
+ avrdude -c arduino -P /dev/ttyUSB2 -p $(AVRDUDE_DEVICE) -e -U flash:w:$<
+
+
+picocom:
+ # picocom sends CR for ENTER -> convert cr (\r) to lf (\n)
+ picocom -b 115200 --omap crlf --raise-dtr /dev/ttyUSB0
+
+picocom0:
+ picocom -b 115200 --omap crlf --raise-dtr /dev/ttyUSB0
+
+picocom1:
+ picocom -b 115200 --omap crlf --raise-dtr /dev/ttyUSB1
+
+picocom2:
+ picocom -b 115200 --omap crlf --raise-dtr /dev/ttyUSB2
+
+
+isp-1284p:
+ avrdude -c usbasp -p $(AVRDUDE_DEVICE)
+
+isp-flash-1284p: dist/$(NAME).elf all
+ avrdude -c usbasp -p $(AVRDUDE_DEVICE) -e -U flash:w:$<
+
+flash-1284p: dist/$(NAME).elf all
+ avrdude -c arduino -p $(AVRDUDE_DEVICE) -P /dev/ttyUSB0 -b 115200 -e -U flash:w:$<
+
+release: dist/$(NAME).elf sim/$(NAME).elf
+ ../create-release release $(word 1, $^) release/sim $(word 2, $^)
+
+clean:
+ @rm -r dist
+ @rm -r build
+ @rm -r sim
+ @find . -type f -name ".depend" -exec rm {} \;
+ @echo "clean done"
--- /dev/null
+../src
\ No newline at end of file
--- /dev/null
+#include "bme280.h"
+#include <util/delay.h>
+#include <stdio.h>
+
+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;
+}
--- /dev/null
+// 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 <Adafruit_I2CDevice.h>
+// #include <Adafruit_SPIDevice.h>
+// #include <Adafruit_Sensor.h>
+
+
+#include "../i2cmaster.hpp"
+#include "../main.hpp"
+// #define byte uint8_t
+
+
+
+#include <stdint.h>
+#include <string.h>
+#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
--- /dev/null
+/*
+ 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 <util/delay.h>
+#include <stdio.h>
+#include <avr/io.h>
+
+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 <n> 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;
+}
--- /dev/null
+/*
+ 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 <stdint.h>
+// #define byte uint8_t
+
+// #if (ARDUINO >= 100)
+// #include "Arduino.h"
+// #else
+// #include "WProgram.h"
+// #endif
+
+// #include <Wire.h>
+
+// 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 <n> 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
--- /dev/null
+/*
+ * 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 <stdint.h>
+#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; /**<Percentage, unit-less (%) */
+ sensors_color_t color; /**< color in RGB component values */
+ float altitude; /**< Distance between a reference datum and a point or
+ object, in meters. */
+ }; ///< Union for the wide ranges of data we can carry
+} sensors_event_t;
+
+/* Sensor details (40 bytes) */
+/** struct sensor_s is used to describe basic information about a specific
+ * sensor. */
+typedef struct {
+ char name[12]; /**< sensor name */
+ int32_t version; /**< version of the hardware + driver */
+ int32_t sensor_id; /**< unique sensor identifier */
+ int32_t type; /**< this sensor's type (ex. SENSOR_TYPE_LIGHT) */
+ float max_value; /**< maximum value of this sensor's value in SI units */
+ float min_value; /**< minimum value of this sensor's value in SI units */
+ float resolution; /**< smallest difference between two values reported by this
+ sensor */
+ int32_t min_delay; /**< min delay in microseconds between events. zero = not a
+ constant rate */
+} sensor_t;
+
+/** @brief Common sensor interface to unify various sensors.
+ * Intentionally modeled after sensors.h in the Android API:
+ * https://github.com/android/platform_hardware_libhardware/blob/master/include/hardware/sensors.h
+ */
+class Adafruit_Sensor {
+public:
+ // Constructor(s)
+ Adafruit_Sensor() {}
+// virtual ~Adafruit_Sensor() {}
+
+// // These must be defined by the subclass
+
+// /*! @brief Whether we should automatically change the range (if possible) for
+// higher precision
+// @param enabled True if we will try to autorange */
+// virtual void enableAutoRange(bool enabled) {
+// (void)enabled; /* suppress unused warning */
+// };
+
+// /*! @brief Get the latest sensor event
+// @returns True if able to fetch an event */
+// virtual bool getEvent(sensors_event_t *) = 0;
+// /*! @brief Get info about the sensor itself */
+// virtual void getSensor(sensor_t *) = 0;
+
+// void printSensorDetails(void);
+};
+
+#endif
--- /dev/null
+#include <avr/io.h>
+#include <avr/pgmspace.h>
+#include <stdio.h>
+
+#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;
+}
+
--- /dev/null
+#ifndef I2C_MASTER
+#define I2C_MASTER
+
+#include <stdint.h>
+
+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
--- /dev/null
+#include <avr/io.h>
+#include <stdio.h>
+#include <util/atomic.h>
+
+#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;
+ }
+
+}
+
--- /dev/null
+#ifndef I2C_SLAVE
+#define I2C_SLAVE
+
+#include <stdint.h>
+
+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
--- /dev/null
+#include <avr/io.h>
+#include <avr/pgmspace.h>
+#include <avr/interrupt.h>
+#include <util/delay.h>
+
+#include <stdio.h>
+#include <util/atomic.h>
+
+#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"
+
+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
+
+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";
+
+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);
+}
+
+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 Hardware-Version: ADC7H = %d (ATmega644P, 3.3V)\n"), ADCH);
+ #elif __AVR_ATmega1284P__
+ printf_P(PSTR("\nInvalid Hardware-Version: ADC7H = %d (ATmega1284P, 3.3V)\n"), ADCH);
+ #elif __AVR_ATmega328P__
+ printf_P(PSTR("\nInvalid Hardware-Version: ADC7H = %d (ATmega328P, 5V)\n"), ADCH);
+ #endif
+ } else {
+ printf_P(PSTR("\n\nHardware %d detected (ADC7H=0x%02X)"), 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 <<TXEN0);
+ UCSR0C = (1 << UCSZ01) | ( 1<< UCSZ00);
+ UBRR0H = 0;
+ UBRR0L = F_CPU / 8 / 115200 - 1;
+
+ TCCR2A = (1 << WGM21);
+ TCCR2B = (1 << CS21); // CTC, F_CPU/8=1.5MHz
+ OCR2A = 150; // 150/1.5E6 = 0.1ms
+ TIMSK2 = (1 << OCIE2A);
+
+ stdout = &mystdout;
+ stdin = &mystdin;
+
+ sei();
+
+ #if defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__)
+ TestUnit *unit[] = {
+ &led, &sw, &rgb, &seg7, &poti, &encoder, &r2r, &motor, &portExp, &lcd, &uart1, &modbus, &ieee485,
+ &i2cMaster, &i2cSlave, &i2cSparkfun, &rtc8563, &cc1101Send, &cc1101Receive
+ };
+ #endif
+
+ #ifdef __AVR_ATmega328P__
+ TestUnit *unit[] = {
+ &led, &sw, &rgb, &seg7, &poti, &encoder, &r2r, &motor, &portExp, &lcd,
+ &i2cMaster, &i2cSlave, &i2cSparkfun
+ };
+ #endif
+
+ detectHardwareVersion();
+
+ while (1) {
+ uint16_t i;
+ char s[4];
+ do {
+ printf_P(PSTR_LINEFEED);
+ printf_P(PSTR_DIVIDER);
+ printf_P(MAIN_CPP_PART_NAME); printf_P(PSTR(" / "));
+ printf_P(MAIN_CPP_DATE); printf_P(PSTR(" / "));
+ printf_P(MAIN_CPP_TIME);
+ printf_P(PSTR_DIVIDER);
+ if (hardwareVersion >= 1 && hardwareVersion <= 2) {
+ printf_P(PSTR("Hardware: %d"), hardwareVersion);
+ } else {
+ printf_P(PSTR("ERROR: Invalid Hardware (%d)"), hardwareVersion);
+ for(;;);
+ }
+ printf_P(PSTR_DIVIDER);
+ printf_P(PSTR_LINEFEED);
+ printf_P(PSTR("Available units:\n\n"));
+ for (i = 0; i < sizeof(unit) / sizeof(unit[0]); 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 >= sizeof(unit) / sizeof(unit[0]) );
+
+ 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 (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;
+ }
+}
+
--- /dev/null
+#ifndef MAIN_HPP
+#define MAIN_HPP
+
+#include <stdint.h>
+#include <avr/pgmspace.h>
+
+#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
--- /dev/null
+#include <stdio.h>
+#include <avr/io.h>
+#include <avr/pgmspace.h>
+#include <util/delay.h>
+
+#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) {
+ 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<<SPIF))) {}
+ return SPDR;
+ }
+
+ void Cc1101::triggerOn () {
+ PORTB |= (1 << PB0);
+ }
+
+ void Cc1101::triggerOff () {
+ PORTB &= ~(1 << PB0);
+ }
+
+ void Cc1101::triggerToggle () {
+ PORTB ^= (1 << PB0);
+ }
+
+ PGM_P Cc1101::getName () {
+ switch (mode) {
+ case Send: return PSTR("CC-1101-Send");
+ case Receive: return PSTR("CC-1101-Receive");
+ case Test: return PSTR("CC-1101-Test");
+ default: return PSTR("CC-1101-?");
+ }
+ }
+
+ void Cc1101::init () {
+
+ #ifdef __AVR_ATmega1284P__
+ // Nano-1284
+ DDRC |= (1 << PC6);
+ PORTC |= (1 << PC6); // modem voltage 3.3V ON
+ #endif
+
+ // trigger for debugging
+ PORTB &= ~(1 << PB0);
+ DDRB |= ( 1 << PB0);
+
+ PRR0 &= (1 << PRSPI);
+
+ // nCs
+ PORTA |= (1 << PA4);
+ DDRA |= (1 << PA4);
+ // PORTB/DDRB must be configured before SPCR !!
+
+ PORTB |= (1 << PB4); // nSS must be HIGH, otherwise SPI master will not become active!!
+ DDRB &= ~(1 << PB6); // MISO
+ DDRB |= (1 << PB7) | (1 << PB5) | (1 << PB4); // SPI SCK (=PB7) and SPI MOSI (=PB5)
+
+ // 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
+
+ // GDO0
+ PORTA |= (1 << PA5);
+ DDRA &= ~(1 << PA5);
+
+ printf_P(PSTR(" reset CC1101 ... "));
+ if (!resetCC1101()) {
+ printf_P(PSTR("fails"));
+ return;
+ }
+ printf_P(PSTR("OK, init register ... "));;
+ if (!initRegister()) {
+ printf_P(PSTR("fails"));
+ return;
+ }
+ printf_P(PSTR("OK, init PATABLE ... "));;
+ if (!initPaTable()) {
+ printf_P(PSTR("fails"));
+ return;
+ }
+ printf_P(PSTR("OK"));
+
+ debugPrint.byte = 0xff; // print all
+ printRegisters();
+ debugPrint.byte = 0x00; // print nothing
+
+ }
+
+ void Cc1101::cleanup () {
+ // trigger for debugging
+ PORTB &= ~(1 << PB0);
+ DDRB &= ~( 1 << PB0);
+
+ SPCR = 0;
+ PORTB &= ~(1 << PB4);
+ DDRB &= ~((1 << PB7) | (1 << PB6) | (1 << PB5) | (1 << PB4));
+ DDRA &= ~((1 << PA5) | (1 << PA4));
+ PORTA &= ~((1 << PA5) | (1 << PA4));
+
+ #ifdef __AVR_ATmega1284P__
+ // Nano-1284
+ DDRC &= ~(1 << PC6);
+ PORTC &= ~(1 << PC6); // modem voltage 3.3V OFF
+ #endif
+ }
+
+ void Cc1101::setChipEnableLow () {
+ PORTA &= ~(1 << PA4);
+ }
+
+ void Cc1101::setChipEnableHigh () {
+ PORTA |= (1 << PA4);
+ }
+
+ bool Cc1101::isMisoHigh () {
+ return (PINB & (1 << PB6)) != 0;
+ }
+
+ bool Cc1101::isGd0High () {
+ return (PINA & (1 << PA5)) != 0;
+ }
+
+ bool Cc1101::waitForMisoLow () {
+ timer = 10;
+ while (isMisoHigh()) {
+ if (timer == 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool Cc1101::waitForGD0High () {
+ timer = 100;
+ while (!isGd0High()) {
+ if (timer == 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool Cc1101::waitForGD0Low () {
+ timer = 100;
+ while (isGd0High()) {
+ if (timer == 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool Cc1101::strobe (StrobeCommand_t command) {
+ bool ok = true;
+ setChipEnableLow();
+ ok &= waitForMisoLow();
+ sendSpiByte(command & 0x3f);
+ setChipEnableHigh();
+ return ok;
+ }
+
+ bool Cc1101::writeRegister (uint8_t addr, uint8_t value) {
+ bool ok = true;
+
+ setChipEnableLow();
+ ok &= waitForMisoLow();
+ sendSpiByte(addr);
+ status.spiResponse.byte = sendSpiByte(value);
+ status.spiResponseValid = 1;
+ setChipEnableHigh();
+
+ if (debugPrint.print.writeRegister) {
+ printf_P(PSTR("\n [writeRegister(0x%02x, 0x%02x) -> "), 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);
+ ok &= waitForMisoLow();
+
+
+ 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
+
+
--- /dev/null
+#ifndef CC1101_HPP
+#define CC1101_HPP
+
+#include <stdint.h>
+#include "../main.hpp"
+#include <avr/pgmspace.h>
+
+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; }
+ 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:
+ Cc1101Mode mode;
+ 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
--- /dev/null
+#include <stdio.h>
+#include <avr/io.h>
+
+#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;
+}
+
--- /dev/null
+#ifndef ENCODER_HPP
+#define ENCODER_PP
+
+#include <stdint.h>
+#include "../main.hpp"
+#include <avr/pgmspace.h>
+
+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
--- /dev/null
+#include <stdio.h>
+#include <avr/io.h>
+#include <avr/pgmspace.h>
+#include <util/delay.h>
+#include <math.h>
+
+#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
+ }
+}
--- /dev/null
+#ifndef I2C_HPP
+#define I2C_HPP
+
+#include <stdint.h>
+#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
--- /dev/null
+#include <stdio.h>
+#include <avr/io.h>
+#include <util/delay.h>
+#include <util/atomic.h>
+
+#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 <<TXEN1);
+ UCSR1C = (1 << UCSZ11) | ( 1<< UCSZ10);
+ // UCSR1C |= (1 <<UPM11); // even Parity
+ // UCSR1C |= (1 << UPM11) | (1 << UPM10); // odd Parity
+ UBRR1H = 0;
+ UBRR1L = F_CPU / 8 / 9600 - 1;
+
+ enabled = 1;
+}
+
+void Ieee485::cleanup () {
+ enabled = 0;
+
+ ADMUX = 0;
+ ADCSRA = 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 Ieee485::run (uint8_t subtest) {
+ if (subtest == 0) {
+ CLR_nRE; CLR_DE;
+ while (wait(500) == EOF) {
+ ADCSRA |= (1 << ADSC); // start ADC
+ while (ADCSRA & (1 << ADSC)) {} // wait for result
+ UCSR1A |= (1 << TXC1);
+ SET_nRE;
+ SET_DE;
+ UDR1 = ADCH;
+ while ((UCSR1A & (1 << TXC1)) == 0) {}
+ CLR_DE;
+ CLR_nRE;
+ printf_P(PSTR("\n => 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
--- /dev/null
+#ifndef IEEE485_HPP
+#define IEEE485_HPP
+
+#include <stdint.h>
+#include "../main.hpp"
+#include <avr/pgmspace.h>
+
+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
--- /dev/null
+#include <stdio.h>
+#include <avr/io.h>
+#include <util/delay.h>
+
+#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
--- /dev/null
+#ifndef LCD_HPP
+#define LCD_HPP
+
+#include <stdint.h>
+#include "../main.hpp"
+#include <avr/pgmspace.h>
+
+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
--- /dev/null
+#include <stdio.h>
+#include <avr/io.h>
+#include <util/delay.h>
+
+#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;
+}
+
--- /dev/null
+#ifndef LED_HPP
+#define LED_HPP
+
+#include <stdint.h>
+#include "../main.hpp"
+#include <avr/pgmspace.h>
+
+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
--- /dev/null
+#include <stdio.h>
+#include <avr/io.h>
+#include <util/delay.h>
+
+#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 <<TXEN1);
+ UCSR1C = (1 << UCSZ11) | ( 1<< UCSZ10);
+ // UCSR1C |= (1 <<UPM11); // even Parity
+ // UCSR1C |= (1 << UPM11) | (1 << UPM10); // odd Parity
+ UBRR1H = 0;
+ UBRR1L = F_CPU / 8 / 9600 - 1;
+
+ enabled = 1;
+ printf_P(PSTR("init"));
+
+ } else if (subtest >= 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 <<UDRE1)) == 0) {}
+ }
+ while ((UCSR1A & (1 << TXC1)) == 0) {}
+ CLR_DE;
+ printf_P(PSTR("\n => 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
--- /dev/null
+#ifndef MODBUS_HPP
+#define MODBUS_HPP
+
+#include <stdint.h>
+#include "../main.hpp"
+#include <avr/pgmspace.h>
+
+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
--- /dev/null
+#include <stdio.h>
+#include <avr/io.h>
+#include <util/atomic.h>
+
+#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;
+}
+
+
--- /dev/null
+#ifndef MOTOR_HPP
+#define MOTOR_HPP
+
+#include <stdint.h>
+#include "../main.hpp"
+#include <avr/pgmspace.h>
+
+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
--- /dev/null
+#include <stdio.h>
+#include <avr/io.h>
+#include <util/delay.h>
+
+#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;
+}
+
--- /dev/null
+#ifndef PORTEXP_HPP
+#define PORTEXP_HPP
+
+#include <stdint.h>
+#include "../main.hpp"
+#include <avr/pgmspace.h>
+
+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
--- /dev/null
+#include <stdio.h>
+#include <avr/io.h>
+
+#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;
+}
+
+
--- /dev/null
+#ifndef POTI_HPP
+#define POTI_PP
+
+#include <stdint.h>
+#include "../main.hpp"
+#include <avr/pgmspace.h>
+
+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
--- /dev/null
+#include <stdio.h>
+#include <avr/io.h>
+
+#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;
+}
+
+
--- /dev/null
+#ifndef R2R_HPP
+#define R2R_PP
+
+#include <stdint.h>
+#include "../main.hpp"
+#include <avr/pgmspace.h>
+
+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
--- /dev/null
+#include <stdio.h>
+#include <avr/io.h>
+
+#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;
+}
+
+
--- /dev/null
+#ifndef RGB_HPP
+#define RGB_PP
+
+#include <stdint.h>
+#include "../main.hpp"
+#include <avr/pgmspace.h>
+
+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
--- /dev/null
+#include <stdio.h>
+#include <avr/io.h>
+#include <avr/pgmspace.h>
+#include <util/delay.h>
+#include <math.h>
+
+#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
+
--- /dev/null
+#ifndef RTC8563_HPP
+#define RTC8563_HPP
+
+#include <stdint.h>
+#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
--- /dev/null
+#include <stdio.h>
+#include <avr/io.h>
+
+#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;
+}
+
+
--- /dev/null
+#ifndef SEG7_HPP
+#define SEG7_HPP
+
+#include <stdint.h>
+#include "../main.hpp"
+#include <avr/pgmspace.h>
+
+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
--- /dev/null
+#include <stdio.h>
+#include <avr/io.h>
+#include <util/delay.h>
+
+#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;
+}
--- /dev/null
+#ifndef SWITCH_HPP
+#define SWITCH_HPP
+
+#include <stdint.h>
+#include "../main.hpp"
+#include <avr/pgmspace.h>
+
+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
--- /dev/null
+#include <stdio.h>
+#include <avr/io.h>
+#include <util/delay.h>
+
+#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 <<TXEN1);
+ UCSR1C = (1 << UCSZ11) | ( 1<< UCSZ10);
+ UBRR1H = 0;
+ UBRR1L = F_CPU / 8 / 115200 - 1;
+ stderr = &mystderr;
+ enabled = 1;
+}
+
+void Uart1::cleanup () {
+ enabled = 0;
+ UCSR1A = 0;
+ UCSR1B = 0;
+ UCSR1C = 0;
+ UBRR1H = 0;
+ UBRR1L = 0;
+ stderr = NULL;
+}
+
+int8_t Uart1::run (uint8_t subtest) {
+ if (subtest == 0) {
+ do {
+ printf_P(PSTR("\n => 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
--- /dev/null
+#ifndef UART1_HPP
+#define UART1_HPP
+
+#include <stdint.h>
+#include "../main.hpp"
+#include <avr/pgmspace.h>
+
+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