summaryrefslogtreecommitdiffstats
path: root/boiler-monster/stm32
diff options
context:
space:
mode:
authorfishsoupisgood <github@madingley.org>2020-09-09 11:53:37 +0100
committerfishsoupisgood <github@madingley.org>2020-09-09 11:53:37 +0100
commit9d87c925a9eaa4fc256be3173c14a20d1469472d (patch)
tree50d63f87a47a0eac3f5b8058850184bcd4e6ee95 /boiler-monster/stm32
parentdafd8cf2fdcdd637cc06f760d318cf8391b1a294 (diff)
downloadheating-9d87c925a9eaa4fc256be3173c14a20d1469472d.tar.gz
heating-9d87c925a9eaa4fc256be3173c14a20d1469472d.tar.bz2
heating-9d87c925a9eaa4fc256be3173c14a20d1469472d.zip
everything, mostly, working
Diffstat (limited to 'boiler-monster/stm32')
-rw-r--r--boiler-monster/stm32/.gitignore15
-rw-r--r--boiler-monster/stm32/Makefile8
-rw-r--r--boiler-monster/stm32/Makefile.include44
-rw-r--r--boiler-monster/stm32/Makefile.rules261
-rw-r--r--boiler-monster/stm32/app/1wire.c392
-rw-r--r--boiler-monster/stm32/app/1wire.h10
-rw-r--r--boiler-monster/stm32/app/Makefile113
-rw-r--r--boiler-monster/stm32/app/adc.c154
-rw-r--r--boiler-monster/stm32/app/boiler.ld31
-rw-r--r--boiler-monster/stm32/app/cmd.c55
-rw-r--r--boiler-monster/stm32/app/commit.c3
-rw-r--r--boiler-monster/stm32/app/ds1820.c93
-rw-r--r--boiler-monster/stm32/app/gdb.script2
-rw-r--r--boiler-monster/stm32/app/led.c101
-rw-r--r--boiler-monster/stm32/app/main.c134
-rw-r--r--boiler-monster/stm32/app/ot.c452
-rw-r--r--boiler-monster/stm32/app/ot_phy_rx.c177
-rw-r--r--boiler-monster/stm32/app/ot_phy_tx.c121
-rw-r--r--boiler-monster/stm32/app/pic.c52
-rw-r--r--boiler-monster/stm32/app/pins.h53
-rw-r--r--boiler-monster/stm32/app/pressure.c164
-rw-r--r--boiler-monster/stm32/app/project.h30
-rw-r--r--boiler-monster/stm32/app/prototypes.h106
-rw-r--r--boiler-monster/stm32/app/ring.c77
-rw-r--r--boiler-monster/stm32/app/ring.h13
-rw-r--r--boiler-monster/stm32/app/stdio.c77
-rw-r--r--boiler-monster/stm32/app/temp.c76
-rw-r--r--boiler-monster/stm32/app/ticker.c105
-rw-r--r--boiler-monster/stm32/app/usart.c175
-rw-r--r--boiler-monster/stm32/app/util.c14
-rw-r--r--boiler-monster/stm32/docs/pinout.txt24
-rw-r--r--boiler-monster/stm32/docs/pm0056.pdfbin0 -> 2098835 bytes
-rw-r--r--boiler-monster/stm32/docs/rm0008.pdfbin0 -> 13016697 bytes
-rw-r--r--boiler-monster/stm32/docs/stm32f103c8.pdfbin0 -> 1697666 bytes
-rw-r--r--boiler-monster/stm32/docs/stm32f103c8t6_pinout_voltage01.pngbin0 -> 679565 bytes
m---------boiler-monster/stm32/libopencm30
36 files changed, 3132 insertions, 0 deletions
diff --git a/boiler-monster/stm32/.gitignore b/boiler-monster/stm32/.gitignore
new file mode 100644
index 0000000..64532f4
--- /dev/null
+++ b/boiler-monster/stm32/.gitignore
@@ -0,0 +1,15 @@
+*.o
+*.d
+*.map
+*.hex
+*.elf
+*.swp
+*~
+*.dfu
+plot/data
+plot/script
+*.bin
+scmversion
+commit.h
+opencm3.build.stamp
+*.orig
diff --git a/boiler-monster/stm32/Makefile b/boiler-monster/stm32/Makefile
new file mode 100644
index 0000000..e1cd497
--- /dev/null
+++ b/boiler-monster/stm32/Makefile
@@ -0,0 +1,8 @@
+
+default:
+ make -C libopencm3
+ make -C app
+
+clean:
+ make -C libopencm3 clean
+ make -C app clean
diff --git a/boiler-monster/stm32/Makefile.include b/boiler-monster/stm32/Makefile.include
new file mode 100644
index 0000000..4f5cbd9
--- /dev/null
+++ b/boiler-monster/stm32/Makefile.include
@@ -0,0 +1,44 @@
+##
+## This file is part of the libopencm3 project.
+##
+## Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
+## Copyright (C) 2010 Piotr Esden-Tempski <piotr@esden.net>
+##
+## This library is free software: you can redistribute it and/or modify
+## it under the terms of the GNU Lesser General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## This library is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU Lesser General Public License for more details.
+##
+## You should have received a copy of the GNU Lesser General Public License
+## along with this library. If not, see <http://www.gnu.org/licenses/>.
+##
+
+LIBNAME = opencm3_stm32f1
+DEFS = -DSTM32F1
+
+FP_FLAGS ?= -msoft-float
+ARCH_FLAGS = -mthumb -mcpu=cortex-m3 $(FP_FLAGS) -mfix-cortex-m3-ldrd
+
+################################################################################
+# OpenOCD specific variables
+
+OOCD ?= openocd
+OOCD_INTERFACE ?= interface/stlink-v2.cfg
+OOCD_BOARD ?= target/stm32f1x.cfg
+
+################################################################################
+# Black Magic Probe specific variables
+# Set the BMP_PORT to a serial port and then BMP is used for flashing
+BMP_PORT ?=
+
+################################################################################
+# texane/stlink specific variables
+#STLINK_PORT ?= :4242
+
+
+include ../Makefile.rules
diff --git a/boiler-monster/stm32/Makefile.rules b/boiler-monster/stm32/Makefile.rules
new file mode 100644
index 0000000..a723e6f
--- /dev/null
+++ b/boiler-monster/stm32/Makefile.rules
@@ -0,0 +1,261 @@
+#
+## This file is part of the libopencm3 project.
+##
+## Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
+## Copyright (C) 2010 Piotr Esden-Tempski <piotr@esden.net>
+## Copyright (C) 2013 Frantisek Burian <BuFran@seznam.cz>
+##
+## This library is free software: you can redistribute it and/or modify
+## it under the terms of the GNU Lesser General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## This library is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU Lesser General Public License for more details.
+##
+## You should have received a copy of the GNU Lesser General Public License
+## along with this library. If not, see <http://www.gnu.org/licenses/>.
+##
+
+# Be silent per default, but 'make V=1' will show all compiler calls.
+ifneq ($(V),1)
+Q := @
+NULL := 2>/dev/null
+endif
+
+###############################################################################
+# Executables
+
+PREFIX ?= arm-none-eabi
+
+CC := $(PREFIX)-gcc
+CXX := $(PREFIX)-g++
+LD := $(PREFIX)-gcc
+AR := $(PREFIX)-ar
+AS := $(PREFIX)-as
+OBJCOPY := $(PREFIX)-objcopy
+OBJDUMP := $(PREFIX)-objdump
+GDB := $(PREFIX)-gdb
+STFLASH = $(shell which st-flash)
+STYLECHECK := /checkpatch.pl
+STYLECHECKFLAGS := --no-tree -f --terse --mailback
+STYLECHECKFILES := $(shell find . -name '*.[ch]')
+
+
+###############################################################################
+# Source files
+
+LDSCRIPT ?= $(BINARY).ld
+
+#OBJS += $(BINARY).o
+
+
+ifeq ($(strip $(OPENCM3_DIR)),)
+# user has not specified the library path, so we try to detect it
+
+# where we search for the library
+LIBPATHS := ./libopencm3 ../libopencm3
+
+OPENCM3_DIR := $(wildcard $(LIBPATHS:=/locm3.sublime-project))
+OPENCM3_DIR := $(firstword $(dir $(OPENCM3_DIR)))
+
+ifeq ($(strip $(OPENCM3_DIR)),)
+$(warning Cannot find libopencm3 library in the standard search paths.)
+$(error Please specify it through OPENCM3_DIR variable!)
+endif
+endif
+
+ifeq ($(V),1)
+$(info Using $(OPENCM3_DIR) path to library)
+endif
+
+INCLUDE_DIR = $(OPENCM3_DIR)/include
+LIB_DIR = $(OPENCM3_DIR)/lib
+SCRIPT_DIR = $(OPENCM3_DIR)/scripts
+
+###############################################################################
+# C flags
+
+CFLAGS += -Os -g
+CFLAGS += -Wextra -Wimplicit-function-declaration
+CFLAGS += -Wmissing-prototypes -Wstrict-prototypes
+CFLAGS += -fno-common -ffunction-sections -fdata-sections
+
+###############################################################################
+# C++ flags
+
+CXXFLAGS += -Os -g
+CXXFLAGS += -Wextra -Wshadow -Wredundant-decls -Weffc++
+CXXFLAGS += -fno-common -ffunction-sections -fdata-sections
+
+###############################################################################
+# C & C++ preprocessor common flags
+
+CPPFLAGS += -MD
+CPPFLAGS += -Wall -Wundef
+
+INCLUDES = -I$(INCLUDE_DIR)
+DEFINES = $(DEFS)
+
+CPPFLAGS += $(INCLUDES) $(DEFINES)
+
+###############################################################################
+# Linker flags
+
+LDFLAGS += --static -nostartfiles
+LDFLAGS += -L$(LIB_DIR)
+LDFLAGS += -T$(LDSCRIPT)
+LDFLAGS += -Wl,-Map=$(*).map
+LDFLAGS += -Wl,--gc-sections
+ifeq ($(V),99)
+LDFLAGS += -Wl,--print-gc-sections
+endif
+
+###############################################################################
+# Used libraries
+
+LDLIBS += -l$(LIBNAME)
+LDLIBS += -Wl,--start-group -lc -lgcc -lnosys -Wl,--end-group
+
+###############################################################################
+###############################################################################
+###############################################################################
+
+.SUFFIXES: .elf .bin .hex .srec .list .map .images .dfu
+.SECONDEXPANSION:
+.SECONDARY:
+
+all: elf
+
+
+elf: $(BINARY).elf
+bin: $(BINARY).bin
+hex: $(BINARY).hex
+srec: $(BINARY).srec
+list: $(BINARY).list
+
+images: $(BINARY).images
+flash: $(BINARY).flash
+
+%.images: %.bin %.hex %.srec %.list %.map %.dfu
+ @#printf "*** $* images generated ***\n"
+
+%.bin: %.elf
+ @#printf " OBJCOPY $(*).bin\n"
+ $(Q)$(OBJCOPY) -Obinary $(*).elf $(*).bin
+
+%.hex: %.elf
+ @#printf " OBJCOPY $(*).hex\n"
+ $(Q)$(OBJCOPY) -Oihex $(*).elf $(*).hex
+
+%.dfu: %.elf
+ @#printf " OBJCOPY $(*).dfu\n"
+ $(Q)$(OBJCOPY) -Obinary $(*).elf $(*).dfu
+
+%.srec: %.elf
+ @#printf " OBJCOPY $(*).srec\n"
+ $(Q)$(OBJCOPY) -Osrec $(*).elf $(*).srec
+
+%.list: %.elf
+ @#printf " OBJDUMP $(*).list\n"
+ $(Q)$(OBJDUMP) -S $(*).elf > $(*).list
+
+fish:
+ echo %.elf %.map: $(OBJS) $(LDSCRIPT) $(LIB_DIR)/lib$(LIBNAME).a
+ echo $(BINARY).elf
+
+%.elf %.map: $(OBJS) $(LDSCRIPT) $(LIB_DIR)/lib$(LIBNAME).a
+ @#printf " LD $(*).elf\n"
+ $(Q)$(LD) $(LDFLAGS) $(ARCH_FLAGS) $(OBJS) $(LDLIBS) -o $(*).elf
+
+%.o: %.c $(OPENCM3_DIR)/../opencm3.build.stamp
+ @#printf " CC $(*).c\n"
+ $(Q)$(CC) $(CFLAGS) $(CPPFLAGS) $(ARCH_FLAGS) -o $(*).o -c $(*).c
+
+%.o: %.cxx $(OPENCM3_DIR)/../opencm3.build.stamp
+ @#printf " CXX $(*).cxx\n"
+ $(Q)$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(ARCH_FLAGS) -o $(*).o -c $(*).cxx
+
+%.o: %.cpp $(OPENCM3_DIR)/../opencm3.build.stamp
+ @#printf " CXX $(*).cpp\n"
+ $(Q)$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(ARCH_FLAGS) -o $(*).o -c $(*).cpp
+
+clean:
+ @#printf " CLEAN\n"
+ $(Q)$(RM) *.o *.d *.elf *.bin *.hex *.srec *.list *.map *~ *.dfu ${EXTRACLEANS}
+
+stylecheck: $(STYLECHECKFILES:=.stylecheck)
+styleclean: $(STYLECHECKFILES:=.styleclean)
+
+# the cat is due to multithreaded nature - we like to have consistent chunks of text on the output
+%.stylecheck: %
+ $(Q)$(SCRIPT_DIR)$(STYLECHECK) $(STYLECHECKFLAGS) $* > $*.stylecheck; \
+ if [ -s $*.stylecheck ]; then \
+ cat $*.stylecheck; \
+ else \
+ rm -f $*.stylecheck; \
+ fi;
+
+%.styleclean:
+ $(Q)rm -f $*.stylecheck;
+
+
+%.stlink-flash: %.bin
+ @printf " FLASH $<\n"
+ $(Q)$(STFLASH) write $(*).bin 0x8000000
+
+ifeq ($(STLINK_PORT),)
+ifeq ($(BMP_PORT),)
+ifeq ($(OOCD_SERIAL),)
+%.flash: %.hex
+ @printf " FLASH $<\n"
+ @# IMPORTANT: Don't use "resume", only "reset" will work correctly!
+ $(Q)$(OOCD) -f $(OOCD_INTERFACE) \
+ -f $(OOCD_BOARD) \
+ -c "init" -c "reset init" \
+ -c "flash write_image erase $(*).hex" \
+ -c "reset" \
+ -c "shutdown" $(NULL)
+else
+%.flash: %.hex
+ @printf " FLASH $<\n"
+ @# IMPORTANT: Don't use "resume", only "reset" will work correctly!
+ $(Q)$(OOCD) -f $(OOCD_INTERFACE) \
+ -f $(OOCD_BOARD) \
+ -c "ft2232_serial $(OOCD_SERIAL)" \
+ -c "init" -c "reset init" \
+ -c "flash write_image erase $(*).hex" \
+ -c "reset" \
+ -c "shutdown" $(NULL)
+endif
+else
+%.flash: %.elf
+ @printf " GDB $(*).elf (flash)\n"
+ $(Q)$(GDB) --batch \
+ -ex 'target extended-remote $(BMP_PORT)' \
+ -x $(SCRIPT_DIR)/black_magic_probe_flash.scr \
+ $(*).elf
+endif
+else
+%.flash: %.elf
+ @printf " GDB $(*).elf (flash)\n"
+ $(Q)$(GDB) --batch \
+ -ex 'target extended-remote $(STLINK_PORT)' \
+ -x $(SCRIPT_DIR)/stlink_flash.scr \
+ $(*).elf
+endif
+
+.PHONY: images clean stylecheck styleclean elf bin hex srec list
+
+-include $(OBJS:.o=.d)
+
+
+$(LIB_DIR)/lib$(LIBNAME).a: $(OPENCM3_DIR)/../opencm3.build.stamp
+
+
+$(OPENCM3_DIR)/../opencm3.build.stamp:
+ ${MAKE} -C ${OPENCM3_DIR}
+ touch $@
+
diff --git a/boiler-monster/stm32/app/1wire.c b/boiler-monster/stm32/app/1wire.c
new file mode 100644
index 0000000..c374c5c
--- /dev/null
+++ b/boiler-monster/stm32/app/1wire.c
@@ -0,0 +1,392 @@
+#include "project.h"
+
+#define USART2_TX GPIO_USART2_TX
+#define USART2_TX_PORT GPIOA
+
+#define BIT_ZERO '0'
+#define BIT_ONE '1'
+#define BIT_UNKNOWN 'U'
+#define BIT_CONFLICT 'C'
+
+
+static int poke;
+static volatile unsigned exchange_timeout;
+
+void onewire_tick (void)
+{
+ static unsigned ticker;
+
+ if (exchange_timeout)
+ exchange_timeout--;
+
+
+ ticker++;
+
+ if (ticker < MS_TO_TICKS (5000))
+ return;
+
+ ticker = 0;
+ poke = 1;
+
+
+}
+
+
+static unsigned usart_send_ready (uint32_t usart)
+{
+ return (USART_SR (usart) & USART_SR_TXE);
+}
+
+
+static unsigned usart_recv_ready (uint32_t usart)
+{
+ return (USART_SR (usart) & USART_SR_RXNE);
+}
+
+
+
+static int onewire_exchange (int tx, unsigned timeout)
+{
+ int rx;
+ (void) USART_SR (USART2);
+ (void) usart_recv (USART2);
+
+
+ exchange_timeout = MS_TO_TICKS (timeout);
+
+ while (!usart_send_ready (USART2))
+ if (!exchange_timeout)
+ return -1;
+
+ usart_send (USART2, tx);
+
+ exchange_timeout = MS_TO_TICKS (timeout);
+
+ while (!usart_recv_ready (USART2))
+ if (!exchange_timeout)
+ return -1;
+
+
+ rx = usart_recv (USART2);
+
+ return rx;
+
+}
+
+/* returns 1 if nothing on bus or error */
+int
+onewire_reset (void)
+{
+ int ret;
+
+ usart_set_baudrate (USART2, 9600);
+ delay_ms (1);
+ ret = onewire_exchange (0xf0, 60);
+ usart_set_baudrate (USART2, 115200);
+ delay_ms (1);
+
+ return (ret == 0xf0);
+}
+
+static void
+onewire_one (void)
+{
+ onewire_exchange (0xff, 3);
+}
+
+static void
+onewire_zero (void)
+{
+ onewire_exchange (0x00, 3);
+}
+
+static int
+onewire_read (void)
+{
+ uint8_t rx;
+
+ rx = onewire_exchange (0xff, 3);
+
+ return (rx == 0xff);
+}
+
+
+void
+onewire_write_byte (uint8_t v)
+{
+ int c;
+
+
+ for (c = 0; c < 8; ++c) {
+ if (v & 1)
+ onewire_one();
+ else
+ onewire_zero();
+
+ v >>= 1;
+ }
+}
+
+uint8_t
+onewire_read_byte (void)
+{
+ uint8_t v = 0;
+ int c;
+
+
+ for (c = 0; c < 8; ++c) {
+ v >>= 1;
+
+ if (onewire_read())
+ v |= 0x80;
+ }
+
+
+ return v;
+}
+
+
+
+void
+onewire_read_bytes (uint8_t *buf, int n)
+{
+ while (n--)
+ * (buf++) = onewire_read_byte();
+}
+
+
+void
+onewire_write_bytes (const uint8_t *buf, int n)
+{
+ while (n--)
+ onewire_write_byte (* (buf++));
+}
+
+
+int onewire_select (const Onewire_addr *a)
+{
+ if (!a)
+ onewire_write_byte (ONEWIRE_SKIP_ROM);
+ else {
+ onewire_write_byte (ONEWIRE_MATCH_ROM);
+ onewire_write_bytes (a->a, 8);
+ }
+
+ return 0;
+}
+
+
+int onewire_reset_and_select (const Onewire_addr *a)
+{
+ if (onewire_reset())
+ return 1;
+
+ if (onewire_select (a))
+ return 1;
+
+ return 0;
+}
+
+
+
+int onewire_wait_complete (unsigned timeout)
+{
+ while (! (onewire_read())) {
+ delay_ms (10);
+
+ if (! (timeout--))
+ return 1;
+ }
+
+ return 0;
+}
+
+
+
+int
+onewire_check_crc (uint8_t *buf, int n, uint8_t v)
+{
+ uint8_t crc = 0;
+ int i;
+
+ while (n--) {
+ uint8_t v = * (buf++);
+
+ for (i = 0; i < 8; ++i) {
+ uint8_t mix = (crc ^ v) & 0x01;
+ crc >>= 1;
+
+ if (mix)
+ crc ^= 0x8C;
+
+ v >>= 1;
+ }
+ }
+
+ return ! (crc == v);
+
+}
+
+static int onewire_conduct_search (uint8_t *bits)
+{
+ unsigned i, ir, r;
+
+ if (onewire_reset())
+ return -1;
+
+ onewire_write_byte (ONEWIRE_SEARCH_ROM);
+
+
+ for (i = 0; i < 64; ++i) {
+
+ r = onewire_read();
+ ir = onewire_read();
+
+#if 0
+
+ if ((i == 27) || (i == 24) || (i == 39))
+ ir = r = 0;
+
+#endif
+
+ switch (bits[i]) {
+ case BIT_UNKNOWN:
+
+ if (!r && ir) { /* Zero */
+ bits[i] = BIT_ZERO;
+ onewire_zero();
+ } else if (r && !ir) { /*One */
+ bits[i] = BIT_ONE;
+ onewire_one();
+ } else if (r && ir) { /*Nothing here */
+ //MEH;
+ return -1;
+ } else if (!r && !ir) { /*Both */
+ bits[i] = BIT_CONFLICT;
+ onewire_zero();
+ }
+
+ break;
+
+ case BIT_CONFLICT:
+ if (!r && !ir) /*Both */
+ onewire_zero();
+ else {
+ //MEH;
+ return -1;
+ }
+
+ break;
+
+ case BIT_ZERO:
+ if (!r)
+ onewire_zero();
+ else {
+ //MEH;
+ return -1;
+ }
+
+ break;
+
+ case BIT_ONE:
+ if (!ir)
+ onewire_one();
+ else {
+ //MEH;
+ return -1;
+ }
+
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int onewire_next (uint8_t *bits)
+{
+ unsigned i;
+
+ for (i = 0; i < 64; ++i) {
+
+ if (bits[63 - i] == BIT_CONFLICT) {
+ bits[63 - i] = BIT_ONE;
+ return 1;
+ }
+
+ bits[63 - i] = BIT_UNKNOWN;
+ }
+
+ return 0;
+}
+
+static void onewire_bits_to_a (uint8_t *bits, Onewire_addr *a)
+{
+ unsigned i, j, c;
+
+ for (i = 0, j = 0; i < 8; ++i) {
+
+ a->a[i] = 0;
+
+ for (c = 1; c < 0x100; c <<= 1, ++j) {
+ if (bits[j] == BIT_ONE)
+ a->a[i] |= c;
+ }
+ }
+}
+
+int onewire_search (void)
+{
+ uint8_t bits[64];
+ Onewire_addr a;
+ int r, c;
+
+ delay_ms (2000);
+
+ memset (bits, BIT_UNKNOWN, sizeof (bits));
+
+ do {
+ r = onewire_conduct_search (bits);
+
+ if (!r) {
+ onewire_bits_to_a (bits, &a);
+ c = onewire_check_crc (&a.a[0], 7, a.a[7]);
+
+ if (!c) {
+ printf ("QO: {0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x}\r\n",
+ a.a[0],
+ a.a[1],
+ a.a[2],
+ a.a[3],
+ a.a[4],
+ a.a[5],
+ a.a[6],
+ a.a[7]);
+ }
+
+ }
+
+ } while (onewire_next (bits));
+
+
+ return 0;
+}
+
+
+
+void
+onewire_init (void)
+{
+ MAP_AF_OD (USART2_TX);
+
+ usart_set_baudrate (USART2, 115200);
+ usart_set_databits (USART2, 8);
+ usart_set_stopbits (USART2, USART_STOPBITS_1);
+ usart_set_parity (USART2, USART_PARITY_NONE);
+ usart_set_flow_control (USART2, USART_FLOWCONTROL_NONE);
+ usart_set_mode (USART2, USART_MODE_TX_RX);
+
+ USART_CR3 (USART2) |= USART_CR3_HDSEL;
+
+ usart_enable (USART2);
+}
diff --git a/boiler-monster/stm32/app/1wire.h b/boiler-monster/stm32/app/1wire.h
new file mode 100644
index 0000000..4a31418
--- /dev/null
+++ b/boiler-monster/stm32/app/1wire.h
@@ -0,0 +1,10 @@
+#define ONEWIRE_SKIP_ROM 0xcc
+#define ONEWIRE_SEARCH_ROM 0xf0
+#define ONEWIRE_MATCH_ROM 0x55
+
+#define ONEWIRE_ADDR_LEN 8
+typedef struct {
+ uint8_t a[ONEWIRE_ADDR_LEN];
+} Onewire_addr;
+
+
diff --git a/boiler-monster/stm32/app/Makefile b/boiler-monster/stm32/app/Makefile
new file mode 100644
index 0000000..23b4a74
--- /dev/null
+++ b/boiler-monster/stm32/app/Makefile
@@ -0,0 +1,113 @@
+##
+## This file is part of the libopencm3 project.
+##
+## Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
+##
+## This library is free software: you can redistribute it and/or modify
+## it under the terms of the GNU Lesser General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## This library is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU Lesser General Public License for more details.
+##
+## You should have received a copy of the GNU Lesser General Public License
+## along with this library. If not, see <http://www.gnu.org/licenses/>.
+##
+
+CPROTO=cproto
+PROG=boiler
+
+CSRCS=main.c ot.c ot_phy_rx.c ot_phy_tx.c led.c ticker.c usart.c ring.c stdio.c util.c commit.c cmd.c pic.c 1wire.c temp.c ds1820.c pressure.c adc.c
+HSRCS=pins.h project.h ring.h
+
+EXTRACLEANS=*.orig scmversion commit.h
+
+TARGET=boiler-monster
+
+V=1
+default: ${PROG}.fw.bin
+ rsync -vaP ${PROG}.fw.bin ${TARGET}:/tmp/boiler.fw
+ ssh ${TARGET} /etc/stm32/startup /tmp/boiler.fw
+
+
+install: ${PROG}.fw.bin
+ rsync -vaP ${PROG}.fw.bin ${TARGET}:/etc/stm32/boiler.fw
+
+
+128k_of_ff.bin:
+ dd if=/dev/zero bs=128k count=1 | tr '\0' '\377' > $@
+
+
+${PROG}.fw.bin:${PROG}.bin 128k_of_ff.bin
+ cat ${PROG}.bin 128k_of_ff.bin | dd of=$@ bs=128k iflag=fullblock count=1
+
+
+program: ${PROG}.elf
+ echo halt | nc -t localhost 4444
+ echo flash write_image erase ${PWD}/$< 0x2000 | nc -t localhost 4444
+ echo reset halt | nc -t localhost 4444
+ RV=$$(arm-none-eabi-objdump --disassemble=reset_handler $< | awk '/^0/ { print "0x"$$1 }') && \
+ echo resume $${RV} | nc -t localhost 4444 && \
+ echo reset vector at $${RV}
+
+run:${PROG}.run
+
+%.run: %.hex
+ RV=$$(arm-none-eabi-objdump --disassemble=reset_handler $(*).elf | awk '/^0/ { print "0x"$$1 }') && \
+ $(Q)$(OOCD) -f $(OOCD_INTERFACE) \
+ -f $(OOCD_BOARD) \
+ -c "init" -c "reset init" \
+ -c "flash write_image erase $(*).hex" \
+ -c "reset halt" \
+ -c "resume $${RV}" \
+ -c "shutdown" $(NULL) && \
+ echo reset_handler is at $${RV}
+
+
+
+
+BINARY = ${PROG}
+OBJS = ${CSRCS:%.c=%.o}
+
+include ../Makefile.include
+
+CFLAGS+=-Wno-redundant-decls -Wno-unused-parameter -DDEBUG
+
+INCLUDES += -I..
+
+protos:
+ echo -n > prototypes.h
+ ${CPROTO} $(INCLUDES) $(DEFINES) -e -v ${CSRCS} > prototypes.h.tmp
+ mv -f prototypes.h.tmp prototypes.h
+
+server:
+ $(OOCD) -f $(OOCD_INTERFACE) \
+ -f $(OOCD_BOARD)
+
+debug: ${PROG}.elf
+ ${PREFIX}-gdb -x gdb.script ${PROG}.elf
+
+tidy:
+ astyle -A3 -s2 --attach-extern-c -L -c -w -Y -m0 -f -p -H -U -k3 -xj -xd ${CSRCS} ${HSRCS}
+
+HEAD_REF = $(shell git rev-parse --verify --short HEAD)
+SCM_DIRTY = $(shell if ! git diff-index --quiet HEAD --; then echo "+dirty"; fi)
+SCMVERSION = ${HEAD_REF}${SCM_DIRTY}
+
+scmversion:
+ echo -n ${SCMVERSION} > $@
+
+.PHONY: scmversion
+
+commit.o: commit.h
+
+commit.h: scmversion
+ echo -n '#define COMMIT_ID "' > $@
+ echo -n `cat scmversion` >> $@
+ echo '"' >> $@
+
+
+
diff --git a/boiler-monster/stm32/app/adc.c b/boiler-monster/stm32/app/adc.c
new file mode 100644
index 0000000..2cfc4e3
--- /dev/null
+++ b/boiler-monster/stm32/app/adc.c
@@ -0,0 +1,154 @@
+#include "project.h"
+
+#define T do { printf("%s:%d\r\n",__FILE__,__LINE__); } while (0)
+
+void adc_dump (void)
+{
+ printf ("ADC_SR %x ADC_CR1 %x ADC_CR2 %x\r\n", (unsigned) ADC_SR (ADC1),
+ (unsigned) ADC_CR1 (ADC1),
+ (unsigned) ADC_CR2 (ADC1));
+
+}
+
+
+volatile unsigned timeout;
+
+
+void adc_tick (void)
+{
+ if (timeout) timeout--;
+}
+
+
+static int adc_wait (volatile uint32_t *reg, uint32_t wait_while_set, uint32_t wait_while_clear, unsigned ms)
+{
+ timeout = MS_TO_TICKS (ms);
+
+
+ while ((*reg) & wait_while_set)
+ if (!timeout) {
+ printf ("QADC timeout\r\n");
+ return -1;
+ }
+
+
+ while ((~ (*reg)) & wait_while_clear)
+ if (!timeout) {
+ printf ("QADC timeout\r\n");
+ return -1;
+ }
+
+
+ return 0;
+}
+
+
+
+int adc_calibrate (void)
+{
+ adc_off (ADC1);
+ adc_power_on (ADC1);
+ delay_ms (5);
+
+ ADC_CR2 (ADC1) |= ADC_CR2_RSTCAL;
+
+ if (adc_wait (&ADC_CR2 (ADC1), ADC_CR2_CAL, 0, 2)) return -1;
+
+ ADC_CR2 (ADC1) |= ADC_CR2_CAL;
+
+ if (adc_wait (&ADC_CR2 (ADC1), ADC_CR2_CAL, 0, 2)) return -1;
+
+ return 0;
+}
+
+
+
+int adc_convert_start (unsigned channel)
+{
+ uint8_t ch = channel;
+
+ adc_set_regular_sequence (ADC1, 1, &ch);
+
+ /* Start conversion on regular channels. */
+ ADC_CR2 (ADC1) |= ADC_CR2_SWSTART;
+
+#if 0
+ if (adc_wait (&ADC_CR2 (ADC1), ADC_CR2_SWSTART, 0, 2))
+ return -1;
+#endif
+
+ return 0;
+}
+
+
+
+int adc_convert_done(void)
+{
+
+ if (ADC_SR(ADC1) & ADC_SR_EOC)
+ return 1;
+
+ return 0;
+}
+
+unsigned adc_convert_get(void)
+{
+ return adc_read_regular (ADC1);
+}
+
+
+unsigned adc_convert (unsigned channel)
+{
+ uint8_t ch = channel;
+
+ adc_calibrate();
+
+ adc_set_regular_sequence (ADC1, 1, &ch);
+
+ /* Start conversion on regular channels. */
+ ADC_CR2 (ADC1) |= ADC_CR2_SWSTART;
+
+ if (adc_wait (&ADC_CR2 (ADC1), ADC_CR2_SWSTART, 0, 2))
+ return 0;
+
+ if (adc_wait (&ADC_SR (ADC1), 0, ADC_SR_EOC, 2))
+ return 0;
+
+ return adc_read_regular (ADC1);
+}
+
+void adc_init (void)
+{
+ /* main set ADC clock is 9Mhz */
+
+ adc_dump();
+
+ adc_off (ADC1);
+ rcc_periph_clock_enable (RCC_ADC1);
+#if 0
+ rcc_peripheral_reset (&RCC_APB2RSTR, RCC_APB2RSTR_ADC1RST);
+ rcc_peripheral_clear_reset (&RCC_APB2RSTR, RCC_APB2RSTR_ADC1RST);
+#endif
+
+ adc_set_dual_mode (ADC_CR1_DUALMOD_IND);
+ adc_disable_scan_mode (ADC1);
+ adc_enable_temperature_sensor (ADC1);
+ adc_set_single_conversion_mode (ADC1);
+
+ adc_set_sample_time (ADC1, ADC_CHANNEL0, ADC_SMPR_SMP_239DOT5CYC);
+ adc_set_sample_time (ADC1, ADC_CHANNEL17, ADC_SMPR_SMP_239DOT5CYC); /*Want 17.1uS , which is 154 cycles */
+ adc_enable_external_trigger_regular (ADC1, ADC_CR2_EXTSEL_SWSTART);
+
+ adc_set_right_aligned (ADC1);
+
+
+ adc_dump();
+
+ adc_calibrate();
+
+ adc_dump();
+
+
+
+}
+
diff --git a/boiler-monster/stm32/app/boiler.ld b/boiler-monster/stm32/app/boiler.ld
new file mode 100644
index 0000000..e15beca
--- /dev/null
+++ b/boiler-monster/stm32/app/boiler.ld
@@ -0,0 +1,31 @@
+/*
+ * This file is part of the libopencm3 project.
+ *
+ * Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * This library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Linker script for Olimex STM32-H103 (STM32F103RBT6, 128K flash, 20K RAM). */
+
+/* Define memory regions. */
+MEMORY
+{
+ rom (rx) : ORIGIN = 0x08000000, LENGTH = 128K
+ ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K
+}
+
+/* Include the common ld script. */
+INCLUDE libopencm3_stm32f1.ld
+
diff --git a/boiler-monster/stm32/app/cmd.c b/boiler-monster/stm32/app/cmd.c
new file mode 100644
index 0000000..7e1975a
--- /dev/null
+++ b/boiler-monster/stm32/app/cmd.c
@@ -0,0 +1,55 @@
+#include "project.h"
+
+
+static void cmd_dispatch (char *cmd)
+{
+
+ printf ("Q received cmd %s\r\n", cmd);
+
+ if (!strncmp (cmd, "CH=", 3)) {
+ ot_override_ch = atoi (cmd + 3);
+ printf ("Q CH override set to %d\r\n", ot_override_ch);
+ }
+
+
+ if (!strncmp (cmd, "DHW=", 4)) {
+ ot_override_dhw = atoi (cmd + 4);
+ printf ("Q DHW override set to %d\r\n", ot_override_dhw);
+ }
+
+ if (!strcmp (cmd, "PIC")) {
+ ot_override_dhw = atoi (cmd + 4);
+ printf ("Q Entering PIC mode, reset to leave\r\n");
+ pic_passthru();
+ }
+
+}
+
+
+void cmd_usart_dispatch (void)
+{
+ static char cmd[16];
+ static unsigned cmd_ptr;
+
+ uint8_t c;
+
+ if (ring_read_byte (&rx1_ring, &c))
+ return;
+
+
+ if ((c == '\n') || (c == '\r')) {
+ if (cmd_ptr)
+ cmd_dispatch (cmd);
+
+ cmd_ptr = 0;
+ return;
+ }
+
+
+ if (cmd_ptr < (sizeof (cmd) - 1)) {
+ cmd[cmd_ptr++] = c;
+ cmd[cmd_ptr] = 0;
+ }
+}
+
+
diff --git a/boiler-monster/stm32/app/commit.c b/boiler-monster/stm32/app/commit.c
new file mode 100644
index 0000000..207448f
--- /dev/null
+++ b/boiler-monster/stm32/app/commit.c
@@ -0,0 +1,3 @@
+#include "commit.h"
+
+char scm_version[] = COMMIT_ID;
diff --git a/boiler-monster/stm32/app/ds1820.c b/boiler-monster/stm32/app/ds1820.c
new file mode 100644
index 0000000..200f4b7
--- /dev/null
+++ b/boiler-monster/stm32/app/ds1820.c
@@ -0,0 +1,93 @@
+#include "project.h"
+
+#define DS1820_READ_SCRATCHPAD 0xbe
+#define DS1820_CONVERT_T 0x44
+
+
+#define TIMEOUT 150
+
+
+
+static unsigned extract_leu16 (uint8_t *d)
+{
+ uint32_t u;
+
+ u = (uint32_t) d[0] | (((uint32_t) d[1]) << 8);
+ return u;
+}
+
+
+
+static int extract_les16 (uint8_t *d)
+{
+ uint32_t u;
+ u = extract_leu16 (d);
+
+ if (u & 0x8000) u |= 0xffff0000;
+
+ return (int) u;
+}
+
+
+
+
+
+static int
+ds1820_read_sp (const Onewire_addr *a, unsigned page, uint8_t *buf, unsigned len)
+{
+ if (onewire_reset_and_select (a))
+ return ~0U;
+
+ onewire_write_byte (DS1820_READ_SCRATCHPAD);
+ onewire_read_bytes (buf, len);
+
+ if ((len == 9) && onewire_check_crc (buf, 8, buf[8]))
+ return ~0U;
+
+ return 0;
+}
+
+
+
+static int ds1820_convert_t (const Onewire_addr *a)
+{
+
+ if (onewire_reset_and_select (a))
+ return ~0U;
+
+ onewire_write_byte (DS1820_CONVERT_T);
+
+ if (onewire_wait_complete (TIMEOUT))
+ return ~0U;
+
+ return 0;
+}
+
+int
+ds1820_read (const Onewire_addr *a, int *temp)
+{
+ uint8_t buf[9];
+ int t;
+
+ if (ds1820_read_sp (a, 0, buf, 9))
+ return 1;
+
+ if (ds1820_convert_t (a))
+ return 1;
+
+ if (ds1820_read_sp (a, 0, buf, 9))
+ return 1;
+
+ t = extract_les16 (&buf[0]);
+
+ t <<= 4 ;
+
+ if (temp)
+ *temp = t;
+
+
+ return 0;
+}
+
+
+
diff --git a/boiler-monster/stm32/app/gdb.script b/boiler-monster/stm32/app/gdb.script
new file mode 100644
index 0000000..7cf9d09
--- /dev/null
+++ b/boiler-monster/stm32/app/gdb.script
@@ -0,0 +1,2 @@
+target remote localhost:3333
+cont
diff --git a/boiler-monster/stm32/app/led.c b/boiler-monster/stm32/app/led.c
new file mode 100644
index 0000000..bdfd084
--- /dev/null
+++ b/boiler-monster/stm32/app/led.c
@@ -0,0 +1,101 @@
+#include "project.h"
+
+#define LED_BOARD GPIO13
+#define LED_BOARD_PORT GPIOC
+
+#define LED_YELLOW GPIO11
+#define LED_YELLOW_PORT GPIOA
+
+#define LED_GREEN1 GPIO12
+#define LED_GREEN1_PORT GPIOA
+
+#define LED_GREEN2 GPIO15
+#define LED_GREEN2_PORT GPIOA
+
+#define LED_RED GPIO3
+#define LED_RED_PORT GPIOB
+
+static unsigned led, yellow;
+
+
+void led_red_set (int i)
+{
+ if (i)
+ CLEAR (LED_RED);
+ else
+ SET (LED_RED);
+}
+
+void led_green1_set (int i)
+{
+ if (i)
+ CLEAR (LED_GREEN1);
+ else
+ SET (LED_GREEN1);
+}
+
+void led_green2_set (int i)
+{
+ if (i)
+ CLEAR (LED_GREEN2);
+ else
+ SET (LED_GREEN2);
+}
+
+static void _led_yellow_set (int i)
+{
+ if (i)
+ CLEAR (LED_YELLOW);
+ else
+ SET (LED_YELLOW);
+}
+
+static void led_board_set (int i)
+{
+ if (i)
+ CLEAR (LED_BOARD);
+ else
+ SET (LED_BOARD);
+}
+
+void led_blink (void)
+{
+ led = MS_TO_TICKS (25);
+}
+
+
+void led_yellow_set (int i)
+{
+ yellow = !!i;
+}
+
+
+void
+led_tick (void)
+{
+ if (led) {
+ led--;
+
+ led_board_set (1);
+ _led_yellow_set (!yellow);
+ } else {
+
+ led_board_set (0);
+ _led_yellow_set (yellow);
+ }
+}
+
+
+void
+led_init (void)
+{
+ MAP_OUTPUT_PP (LED_BOARD);
+ MAP_OUTPUT_PP (LED_RED);
+ MAP_OUTPUT_PP (LED_GREEN1);
+ MAP_OUTPUT_PP (LED_GREEN2);
+ MAP_OUTPUT_PP (LED_YELLOW);
+
+ CLEAR (LED_BOARD);
+}
+
+
diff --git a/boiler-monster/stm32/app/main.c b/boiler-monster/stm32/app/main.c
new file mode 100644
index 0000000..5ceb4af
--- /dev/null
+++ b/boiler-monster/stm32/app/main.c
@@ -0,0 +1,134 @@
+#include "project.h"
+extern uint32_t dfu_flag;
+
+extern volatile uint32_t vector_table[];
+
+
+void reset_hardware (void)
+{
+ int i;
+
+ rcc_periph_reset_pulse (RST_OTGFS);
+ rcc_periph_reset_pulse (RST_ETHMAC);
+ rcc_periph_reset_pulse (RST_AFIO);
+ rcc_periph_reset_pulse (RST_GPIOA);
+ rcc_periph_reset_pulse (RST_GPIOB);
+ rcc_periph_reset_pulse (RST_GPIOC);
+ rcc_periph_reset_pulse (RST_GPIOD);
+ rcc_periph_reset_pulse (RST_GPIOE);
+ rcc_periph_reset_pulse (RST_GPIOF);
+ rcc_periph_reset_pulse (RST_GPIOG);
+ rcc_periph_reset_pulse (RST_ADC1);
+ rcc_periph_reset_pulse (RST_ADC2);
+ rcc_periph_reset_pulse (RST_TIM1);
+ rcc_periph_reset_pulse (RST_SPI1);
+ rcc_periph_reset_pulse (RST_TIM8);
+ rcc_periph_reset_pulse (RST_USART1);
+ rcc_periph_reset_pulse (RST_ADC3);
+ rcc_periph_reset_pulse (RST_TIM15);
+ rcc_periph_reset_pulse (RST_TIM16);
+ rcc_periph_reset_pulse (RST_TIM17);
+ rcc_periph_reset_pulse (RST_TIM9);
+ rcc_periph_reset_pulse (RST_TIM10);
+ rcc_periph_reset_pulse (RST_TIM11);
+ rcc_periph_reset_pulse (RST_TIM2);
+ rcc_periph_reset_pulse (RST_TIM3);
+ rcc_periph_reset_pulse (RST_TIM4);
+ rcc_periph_reset_pulse (RST_TIM5);
+ rcc_periph_reset_pulse (RST_TIM6);
+ rcc_periph_reset_pulse (RST_TIM7);
+ rcc_periph_reset_pulse (RST_TIM12);
+ rcc_periph_reset_pulse (RST_TIM13);
+ rcc_periph_reset_pulse (RST_TIM14);
+ rcc_periph_reset_pulse (RST_WWDG);
+ rcc_periph_reset_pulse (RST_SPI2);
+ rcc_periph_reset_pulse (RST_SPI3);
+ rcc_periph_reset_pulse (RST_USART2);
+ rcc_periph_reset_pulse (RST_USART3);
+ rcc_periph_reset_pulse (RST_UART4);
+ rcc_periph_reset_pulse (RST_UART5);
+ rcc_periph_reset_pulse (RST_I2C1);
+ rcc_periph_reset_pulse (RST_I2C2);
+ rcc_periph_reset_pulse (RST_USB);
+ rcc_periph_reset_pulse (RST_CAN);
+ rcc_periph_reset_pulse (RST_CAN1);
+ rcc_periph_reset_pulse (RST_CAN2);
+ rcc_periph_reset_pulse (RST_BKP);
+ rcc_periph_reset_pulse (RST_PWR);
+ rcc_periph_reset_pulse (RST_DAC);
+ rcc_periph_reset_pulse (RST_CEC);
+
+
+ for (i = 0; i < NVIC_IRQ_COUNT; ++i)
+ nvic_disable_irq (i);
+
+}
+
+
+int
+main (void)
+{
+
+ SCB_VTOR = (uint32_t) &vector_table;
+ asm volatile ("msr msp, %0"::"g" (vector_table[0]));
+
+ reset_hardware();
+
+
+
+ // rcc_clock_setup_in_hsi_out_48mhz ();
+ //nvic_set_priority_grouping(NVIC_PriorityGroup_4);
+
+ /*set up pll */
+ rcc_clock_setup_in_hse_8mhz_out_72mhz();
+
+ /*turn on clocks to periferals */
+ rcc_periph_clock_enable (RCC_GPIOA);
+ rcc_periph_clock_enable (RCC_GPIOB);
+ rcc_periph_clock_enable (RCC_GPIOC);
+ rcc_periph_clock_enable (RCC_AFIO);
+ rcc_periph_clock_enable (RCC_USART1);
+ rcc_periph_clock_enable (RCC_USART2);
+ rcc_periph_clock_enable (RCC_USART3);
+ rcc_periph_clock_enable (RCC_ADC1);
+
+ dwt_enable_cycle_counter();
+
+ /*Change interrupt priorities so that USART trumps Timer trumps ATKBD */
+ nvic_set_priority (NVIC_USART1_IRQ, 0x40);
+ nvic_set_priority (NVIC_USART2_IRQ, 0x40);
+ nvic_set_priority (NVIC_USART3_IRQ, 0x40);
+ nvic_set_priority (NVIC_SYSTICK_IRQ, 0x80);
+ nvic_set_priority (NVIC_EXTI9_5_IRQ, 0xc0);
+
+ gpio_primary_remap (AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_ON, 0);
+
+ led_init();
+
+ usart_init();
+ ticker_init();
+
+ pic_init();
+ ot_init();
+
+ onewire_init();
+
+ pressure_init();
+ adc_init();
+
+
+ printf ("STARTUP\r\n");
+ printf ("%s\r\n", scm_version);
+
+
+ for (;;) {
+ if (!ring_empty (&rx1_ring))
+ cmd_usart_dispatch();
+
+ temp_dispatch();
+
+ }
+
+
+ return 0;
+}
diff --git a/boiler-monster/stm32/app/ot.c b/boiler-monster/stm32/app/ot.c
new file mode 100644
index 0000000..2a8ad36
--- /dev/null
+++ b/boiler-monster/stm32/app/ot.c
@@ -0,0 +1,452 @@
+#include "project.h"
+
+
+typedef struct {
+ uint8_t flags: 5;
+ uint8_t type: 3;
+ uint8_t data[2];
+} OT_Msg;
+
+
+#define FLAGS_PENDING_THM 1U << 0
+#define FLAGS_PROCESSED_THM 1U << 1
+#define FLAGS_PENDING_USR 1U << 2
+#define FLAGS_PROCESSED_USR 1U << 3
+#define FLAGS_PENDING_OVR 1U << 4
+
+#define FLAGS_PENDING (FLAGS_PENDING_THM|FLAGS_PENDING_USR | FLAGS_PENDING_OVR)
+
+#define IDX_MAX 0x300
+
+static OT_Msg ot_thm_req[IDX_MAX];
+static OT_Msg ot_blr_rsp[IDX_MAX];
+
+#define OT_NEXT(a) (((a) < 0x2ff) ? ((a)+1):0)
+#define OT_INDEX(t,id) ((((unsigned) (t) ) <<8 ) | ((unsigned) (id)))
+#define OT_IDX_TO_ID(idx) ((idx) & 0xff)
+#define OT_IDX_TO_TYPE(idx) ((idx) >> 8)
+
+static unsigned ot_req_idx;
+static unsigned in_flight_req_type;
+static unsigned blr_backoff;
+static unsigned ot_status_wdt;
+
+
+#define OT_READ_DATA 0x0
+#define OT_WRITE_DATA 0x1
+#define OT_INVALID_DATA 0x2
+#define OT_READ_ACK 0x4
+#define OT_WRITE_ACK 0x5
+#define OT_DATA_INVALID 0x6
+#define OT_UNKNOWN_DATAID 0x7
+
+
+#define OT_IDX_STATUS 0
+/*TX*/
+#define OT_IDX_STATUS_BIT_ENABLE_CH (1U << 0)
+#define OT_IDX_STATUS_BIT_ENABLE_DHW (1U << 1)
+
+/*RX*/
+#define OT_IDX_STATUS_BIT_FAULT (1U << 0)
+#define OT_IDX_STATUS_BIT_CH_MODE (1U << 1)
+#define OT_IDX_STATUS_BIT_DHW_MODE (1U << 2)
+#define OT_IDX_STATUS_BIT_FLAME (1U << 3)
+
+#define OT_IDX_CONTROL_SETPOINT 1
+#define OT_IDX_DHW_SETPOINT 56
+
+
+#define OT_IDX_CH_WATER_PRESSURE 18
+#define OT_IDX_RETURN_WATER_TEMP 28
+#define OT_IDX_SUPPLY_INLET_TEMP 80
+
+
+
+unsigned ot_override_ch = 0;
+unsigned ot_override_dhw = 0;
+
+
+static inline int parity (uint8_t v)
+{
+ return (0x6996u >> ((v ^ (v >> 4)) & 0xf)) & 1;
+}
+
+static void ot_parity (uint8_t *data)
+{
+ int p;
+ p = parity (data[0]);
+ p ^= parity (data[1]);
+ p ^= parity (data[2]);
+ p ^= parity (data[3]);
+
+ if (p) data[0] ^= 0x80;
+}
+
+
+static const char *type_str[8] = {
+ ">Read Data",
+ ">Write Data",
+ ">Invalid Data",
+ ">Reserved",
+ "<Read ACK",
+ "<Write ACK",
+ "<Data Invalid",
+ "<Unknown DataID"
+};
+
+static void ot_debug (char *who, uint8_t *msg, char *what)
+{
+ unsigned type = (msg[0] >> 4) & 7;
+
+
+ printf ("%s%02x%02x%02x%02x %s %s\r\n",
+ who,
+ msg[0],
+ msg[1],
+ msg[2],
+ msg[3],
+ type_str[type],
+ what
+ );
+}
+
+static void send_reply_to_thm (unsigned idx)
+{
+ uint8_t reply[4];
+
+ if (ot_tx_thm (NULL)) return;
+
+ reply[0] = ot_blr_rsp[idx].type << 4;
+ reply[1] = OT_IDX_TO_ID (idx);
+ reply[2] = ot_blr_rsp[idx].data[0];
+ reply[3] = ot_blr_rsp[idx].data[1];
+
+ ot_debug ("B", reply, "");
+
+ ot_parity (reply);
+ ot_tx_thm (reply);
+
+ ot_blr_rsp[idx].flags &= ~FLAGS_PROCESSED_THM;
+
+}
+
+
+static int send_req_to_blr (unsigned idx)
+{
+ uint8_t req[4];
+
+ if (ot_tx_blr (NULL)) return -1;
+
+
+
+ req[0] = ot_thm_req[ot_req_idx].type << 4;
+ req[1] = OT_IDX_TO_ID (ot_req_idx);
+ req[2] = ot_thm_req[ot_req_idx].data[0];
+ req[3] = ot_thm_req[ot_req_idx].data[1];
+
+ ot_parity (req);
+ ot_debug (" S", req, "");
+
+ return ot_tx_blr (req);
+}
+
+
+
+
+void ot_rx_thm (uint8_t *msg, int error)
+{
+ unsigned type = (msg[0] >> 4) & 7;
+ unsigned id = msg[1];
+
+ unsigned idx;
+
+ if (error) return;
+
+ if (type > 2) {
+ ot_debug ("T", msg, "message type invalid for thermostat");
+ return;
+ }
+
+ if (ot_override_ch) {
+ if ((id == OT_IDX_STATUS) && (type == OT_READ_DATA)) /* Turn on heating */
+ msg[2] |= OT_IDX_STATUS_BIT_ENABLE_CH;
+
+ if ((id == OT_IDX_CONTROL_SETPOINT) && (type == OT_WRITE_DATA)) /* set water temp */
+ msg[2] = ot_override_ch;
+
+ }
+
+ if (ot_override_dhw) {
+ if ((id == OT_IDX_STATUS) && (type == OT_READ_DATA)) /* Turn on hotwater */
+ msg[2] |= OT_IDX_STATUS_BIT_ENABLE_DHW;
+
+ if ((id == OT_IDX_DHW_SETPOINT) && (type == OT_WRITE_DATA)) /* set water temp */
+ msg[2] = ot_override_dhw;
+
+ }
+
+ if ((id == OT_IDX_STATUS) && (type == OT_READ_DATA))
+ ot_status_wdt = 0;
+
+ ot_debug ("T", msg, "");
+
+ idx = OT_INDEX (type, id);
+
+ if (ot_blr_rsp[idx].flags & FLAGS_PROCESSED_THM)
+ send_reply_to_thm (idx);
+
+ else {
+ ot_thm_req[idx].type = type;
+ ot_thm_req[idx].data[0] = msg[2];;
+ ot_thm_req[idx].data[1] = msg[3];;
+ ot_thm_req[idx].flags |= FLAGS_PENDING_THM;
+ }
+}
+
+static int ot_fake_read_ack (unsigned id, uint8_t *msg)
+{
+ unsigned t;
+
+ switch (id) {
+ case OT_IDX_CH_WATER_PRESSURE:
+ t = pressure_ch();
+
+ if (!t) return -1;
+
+ break;
+
+ case OT_IDX_RETURN_WATER_TEMP:
+ t = temp_ch_return();
+
+ if (!t) return -1;
+
+ break;
+
+ case OT_IDX_SUPPLY_INLET_TEMP:
+ t = temp_supply_inlet();
+
+ if (!t) return -1;
+
+ break;
+
+ default:
+ return -1;
+ }
+
+
+
+ msg[0] = OT_READ_ACK << 4;
+ msg[1] = id;
+ msg[2] = t >> 8;
+ msg[3] = t & 0xff;
+
+ return 0;
+}
+
+
+void ot_rx_blr (uint8_t *msg, int error)
+{
+ unsigned type = (msg[0] >> 4) & 7;
+ unsigned id = msg[1];
+
+ unsigned idx;
+
+ if (error) return;
+
+ ot_debug (" A", msg, "");
+
+ idx = OT_INDEX (in_flight_req_type, id);
+
+ if ((in_flight_req_type == OT_READ_DATA) && (type != OT_READ_ACK)) {
+ if (!ot_fake_read_ack (id, msg)) {
+ ot_debug (" A", msg, " (faked)");
+ type = (msg[0] >> 4) & 7;
+ }
+ }
+
+ ot_blr_rsp[idx].type = type;
+ ot_blr_rsp[idx].data[0] = msg[2];;
+ ot_blr_rsp[idx].data[1] = msg[3];;
+ ot_blr_rsp[idx].flags |= FLAGS_PROCESSED_THM | FLAGS_PROCESSED_USR;
+
+ if (ot_thm_req[idx].flags & FLAGS_PENDING_THM) {
+ ot_thm_req[idx].flags &= ~FLAGS_PENDING_THM;
+ send_reply_to_thm (idx);
+ }
+
+ if (ot_thm_req[idx].flags & FLAGS_PENDING_USR) {
+ ot_thm_req[idx].flags &= ~FLAGS_PENDING_USR;
+ //send_reply_to_usr (idx);
+ }
+
+ if (ot_thm_req[idx].flags & FLAGS_PENDING_OVR)
+ ot_thm_req[idx].flags &= ~FLAGS_PENDING_OVR;
+
+ blr_backoff = 0;
+
+}
+
+
+
+static void ot_boiler_worker (void)
+{
+ unsigned i;
+
+ if (blr_backoff) {
+ blr_backoff--;
+ return;
+ }
+
+ if (ot_tx_blr (NULL)) return;
+
+
+ for (i = 0; i < IDX_MAX; ++i, ot_req_idx = OT_NEXT (ot_req_idx)) {
+
+ if (ot_thm_req[ot_req_idx].flags & FLAGS_PENDING) {
+
+ if (!send_req_to_blr (ot_req_idx)) {
+ ot_thm_req[ot_req_idx].flags &= ~FLAGS_PENDING;
+ in_flight_req_type = OT_IDX_TO_TYPE (ot_req_idx);
+ blr_backoff = 10;
+ ot_req_idx = OT_NEXT (ot_req_idx);
+ return;
+ }
+ }
+
+ }
+
+}
+
+
+static void ot_request (unsigned flags, unsigned type, unsigned id, uint8_t *data)
+{
+ unsigned idx = OT_INDEX (type, id);
+
+ ot_thm_req[idx].type = type;
+
+ if (data) {
+ ot_thm_req[idx].data[0] = data[0];
+ ot_thm_req[idx].data[1] = data[1];
+ } else {
+ ot_thm_req[idx].data[0] = 0;
+ ot_thm_req[idx].data[1] = 0;
+ }
+
+ ot_thm_req[idx].flags |= FLAGS_PENDING_USR;
+
+}
+
+void ot_request_usr (unsigned type, unsigned id, uint8_t *data)
+{
+ ot_request (FLAGS_PENDING_USR, type, id, data);
+}
+static void ot_request_ovr (unsigned type, unsigned id, uint8_t *data)
+{
+ ot_request (FLAGS_PENDING_OVR, type, id, data);
+}
+
+
+
+static void ot_force_status (void)
+{
+ uint8_t data[2] = { (ot_override_ch ? OT_IDX_STATUS_BIT_ENABLE_CH : 0) | (ot_override_dhw ? OT_IDX_STATUS_BIT_ENABLE_DHW : 0), 0};
+
+ ot_request_ovr (OT_READ_DATA, OT_IDX_STATUS, data);
+
+}
+
+static void ot_30s_ticker (void)
+{
+ uint8_t data[2];
+
+ printf ("Q ot ticker - push control flags\r\n");
+
+ if (ot_override_ch) {
+ data[0] = ot_override_ch;
+ data[1] = 0;
+ ot_request_ovr (OT_WRITE_DATA, OT_IDX_CONTROL_SETPOINT, data);
+ }
+
+ if (ot_override_dhw) {
+
+ data[0] = ot_override_dhw;
+ data[1] = 0;
+ ot_request_ovr (OT_WRITE_DATA, OT_IDX_DHW_SETPOINT, data);
+ }
+
+}
+
+static void ot_2s_ticker (void)
+{
+ uint8_t msg[4];
+
+ if (!ot_fake_read_ack (OT_IDX_CH_WATER_PRESSURE, msg))
+ ot_debug ("B", msg, " (fake request and reply)");
+
+ if (!ot_fake_read_ack (OT_IDX_RETURN_WATER_TEMP, msg))
+ ot_debug ("B", msg, " (fake request and reply)");
+
+ if (!ot_fake_read_ack (OT_IDX_SUPPLY_INLET_TEMP, msg))
+ ot_debug ("B", msg, " (fake request and reply)");
+}
+
+
+
+
+
+static void ot_4hz_ticker (void)
+{
+ static unsigned thirty, two;
+ thirty++;
+ two++;
+
+ ot_boiler_worker();
+
+ ot_status_wdt++;
+
+ if (ot_status_wdt > 120) {
+ printf ("Q forcing status packet\r\n");
+ ot_force_status();
+ ot_status_wdt = 0;
+ }
+
+
+ if (two >= 8) {
+ ot_2s_ticker();
+ two = 0;
+ }
+
+
+ if (thirty >= 120) {
+ ot_30s_ticker();
+ thirty = 0;
+ }
+
+
+ led_yellow_set (ot_blr_rsp[OT_INDEX (OT_READ_DATA, OT_IDX_STATUS)].data[1] & OT_IDX_STATUS_BIT_FAULT);
+ led_green1_set (ot_blr_rsp[OT_INDEX (OT_READ_DATA, OT_IDX_STATUS)].data[1] & OT_IDX_STATUS_BIT_CH_MODE);
+ led_green2_set (ot_blr_rsp[OT_INDEX (OT_READ_DATA, OT_IDX_STATUS)].data[1] & OT_IDX_STATUS_BIT_DHW_MODE);
+ led_red_set (ot_blr_rsp[OT_INDEX (OT_READ_DATA, OT_IDX_STATUS)].data[1] & OT_IDX_STATUS_BIT_FLAME);
+
+}
+
+
+void ot_tick (void)
+{
+ static unsigned i;
+ i++;
+
+ if (i >= 500) {
+ ot_4hz_ticker();
+ i = 0;
+ }
+
+
+}
+
+
+void ot_init (void)
+{
+ ot_phy_rx_init();
+ ot_phy_tx_init();
+}
diff --git a/boiler-monster/stm32/app/ot_phy_rx.c b/boiler-monster/stm32/app/ot_phy_rx.c
new file mode 100644
index 0000000..9348d24
--- /dev/null
+++ b/boiler-monster/stm32/app/ot_phy_rx.c
@@ -0,0 +1,177 @@
+#include "project.h"
+
+#define OT_THM_IN GPIO6
+#define OT_THM_IN_PORT GPIOB
+
+#define OT_BLR_IN GPIO7
+#define OT_BLR_IN_PORT GPIOB
+
+
+#define OT_RX_IRQ NVIC_EXTI9_5_IRQ
+
+
+typedef struct rx_phy {
+ uint32_t last_cycle;
+ int last_v;
+ unsigned half_bits;
+ unsigned data_bits;
+ int frame_error;
+ uint8_t data[10];
+ int parity;
+} RX_Phy;
+
+
+static RX_Phy p_thm, p_blr;
+
+
+static uint32_t
+cycle_diff (uint32_t a, uint32_t b)
+{
+ return b - a;
+}
+
+
+
+
+static void ot_phy_rx_bit (RX_Phy *p)
+{
+
+
+ if (p->half_bits & 1)
+ p->frame_error = 1;
+
+ if (!p->half_bits) {
+ if (!p->last_v)
+ p->frame_error = 1;
+
+ return;
+ }
+
+ if (p->data_bits < 32) {
+ if (p->last_v) p->data[p->data_bits >> 3] |= 0x80 >> (p->data_bits & 7);
+
+ p->parity ^= p->last_v;
+ } else if (p->data_bits == 32) {
+ if ((!p->last_v) || (p->parity))
+ p->frame_error = 1;
+
+ led_blink();
+
+ if (p == &p_thm)
+ ot_rx_thm (p->data, p->frame_error);
+ else
+ ot_rx_blr (p->data, p->frame_error);
+ }
+
+
+ p->data_bits++;
+
+}
+
+
+static void ot_phy_rx_worker (RX_Phy *p, int v)
+{
+ uint32_t now, diff;
+
+ if (v == p->last_v) return;
+
+
+ now = dwt_read_cycle_counter();
+ diff = cycle_diff (p->last_cycle, now);
+
+
+ if (diff < 10000) return;
+
+ if (diff < 50000) {
+ if (! (p->half_bits & 1)) ot_phy_rx_bit (p);
+
+ p->half_bits++;
+ } else if (diff < 85000) {
+ p->half_bits++;
+ ot_phy_rx_bit (p);
+ p->half_bits++;
+ } else {
+ p->parity = 0;
+ p->half_bits = 0;
+ p->frame_error = 0;
+ p->data_bits = 0;
+ memset (p->data, 0, sizeof (p->data));
+ }
+
+ p->last_cycle = now;
+ p->last_v = v;
+}
+
+
+
+void
+exti9_5_isr (void)
+{
+ int v;
+
+ if (EXTI_PR & OT_THM_IN) {
+ EXTI_PR = OT_THM_IN;
+ v = !GET (OT_THM_IN);
+
+#if 0
+
+ if (v)
+ CLEAR (OT_BLR_OUT);
+ else
+ SET (OT_BLR_OUT);
+
+#endif
+
+ ot_phy_rx_worker (&p_thm, v);
+
+
+ }
+
+ if (EXTI_PR & OT_BLR_IN) {
+ EXTI_PR = OT_BLR_IN;
+ v = !GET (OT_BLR_IN);
+
+#if 0
+
+ if (v)
+ CLEAR (OT_THM_OUT);
+ else
+ SET (OT_THM_OUT);
+
+#endif
+
+ ot_phy_rx_worker (&p_blr, v);
+ }
+
+ return;
+}
+
+
+void ot_phy_rx_tick (void)
+{
+}
+
+
+
+void ot_phy_rx_init (void)
+{
+ MAP_INPUT_PU (OT_THM_IN);
+ MAP_INPUT_PU (OT_BLR_IN);
+
+
+ exti_select_source (OT_THM_IN, OT_THM_IN_PORT);
+ exti_set_trigger (OT_THM_IN, EXTI_TRIGGER_BOTH);
+ exti_enable_request (OT_THM_IN);
+ exti_reset_request (OT_THM_IN);
+
+ exti_select_source (OT_BLR_IN, OT_BLR_IN_PORT);
+ exti_set_trigger (OT_BLR_IN, EXTI_TRIGGER_BOTH);
+ exti_enable_request (OT_BLR_IN);
+ exti_reset_request (OT_BLR_IN);
+
+ nvic_enable_irq (OT_RX_IRQ);
+
+}
+
+
+
diff --git a/boiler-monster/stm32/app/ot_phy_tx.c b/boiler-monster/stm32/app/ot_phy_tx.c
new file mode 100644
index 0000000..36fbcc5
--- /dev/null
+++ b/boiler-monster/stm32/app/ot_phy_tx.c
@@ -0,0 +1,121 @@
+#include "project.h"
+
+#define OT_THM_OUT GPIO9
+#define OT_THM_OUT_PORT GPIOB
+
+#define OT_BLR_OUT GPIO8
+#define OT_BLR_OUT_PORT GPIOB
+
+
+typedef struct tx_phy {
+ int busy;
+ unsigned half_bit;
+ uint8_t data[4];
+} TX_Phy;
+
+
+static TX_Phy p_thm, p_blr;
+
+
+
+#if 0
+static uint32_t
+cycle_diff (uint32_t a, uint32_t b)
+{
+ return b - a;
+}
+#endif
+
+
+static int ot_phy_tx_worker (TX_Phy *p)
+{
+ int ret = p->half_bit & 1;
+ unsigned bit;
+
+
+ if (p->half_bit < 2)
+ ret ^= 1;
+
+ else if (p->half_bit < 66) {
+ bit = (p->half_bit >> 1) - 1;
+
+ if (p->data[bit >> 3] & (0x80 >> (bit & 7)))
+ ret ^= 1;
+ } else if (p->half_bit < 68)
+
+ ret ^= 1;
+
+ p->half_bit++;
+
+ if (p->half_bit == 68) {
+ p->half_bit = 0;
+ p->busy = 0;
+ }
+
+ return ret;
+}
+
+
+void ot_phy_tx_tick (void)
+{
+ int v;
+
+ if (p_thm.busy) {
+ v = ot_phy_tx_worker (&p_thm);
+
+ if (v)
+ CLEAR (OT_THM_OUT);
+ else
+ SET (OT_THM_OUT);
+ }
+
+
+ if (p_blr.busy) {
+ v = ot_phy_tx_worker (&p_blr);
+
+ if (v)
+ CLEAR (OT_BLR_OUT);
+ else
+ SET (OT_BLR_OUT);
+
+ }
+}
+
+
+int ot_tx_thm (uint8_t *data)
+{
+ if (p_thm.busy) return -1;
+
+ if (!data) return 0;
+
+ led_blink();
+
+ memcpy (p_thm.data, data, sizeof (p_thm.data));
+ p_thm.busy = 1;
+
+ return 0;
+}
+
+
+int ot_tx_blr (uint8_t *data)
+{
+
+ if (p_blr.busy) return -1;
+
+ if (!data) return 0;
+
+ led_blink();
+
+ memcpy (p_blr.data, data, sizeof (p_blr.data));
+ p_blr.busy = 1;
+
+ return 0;
+}
+
+
+
+void ot_phy_tx_init (void)
+{
+ MAP_OUTPUT_PP (OT_THM_OUT);
+ MAP_OUTPUT_PP (OT_BLR_OUT);
+}
diff --git a/boiler-monster/stm32/app/pic.c b/boiler-monster/stm32/app/pic.c
new file mode 100644
index 0000000..b478766
--- /dev/null
+++ b/boiler-monster/stm32/app/pic.c
@@ -0,0 +1,52 @@
+#include "project.h"
+
+#define PIC_RESET GPIO1
+#define PIC_RESET_PORT GPIOB
+
+
+static void pic_reset (void)
+{
+ SET (PIC_RESET);
+ delay_ms (1);
+ CLEAR (PIC_RESET);
+}
+
+
+void pic_passthru (void)
+{
+ uint8_t c;
+
+ printf ("\r\nPIC MODE\r\n");
+ usart1_drain();
+ block_stdio = 1;
+
+ while (!ring_read_byte (&rx3_ring, &c));
+
+ pic_reset();
+
+
+ for (;;) {
+ if (!ring_read_byte (&rx1_ring, &c))
+ usart3_queue (c);
+
+ if (!ring_read_byte (&rx3_ring, &c))
+ usart1_queue (c);
+
+ }
+}
+
+
+
+
+
+
+
+void pic_init (void)
+{
+ MAP_OUTPUT_PP (PIC_RESET);
+
+ pic_reset();
+
+}
+
+
diff --git a/boiler-monster/stm32/app/pins.h b/boiler-monster/stm32/app/pins.h
new file mode 100644
index 0000000..9ea01cf
--- /dev/null
+++ b/boiler-monster/stm32/app/pins.h
@@ -0,0 +1,53 @@
+#ifndef _PINS_H_
+#define _PINS_H_
+
+/* st seem to change these with every chip revision */
+
+#define MAP_AF(a) do { \
+ gpio_set_mode( a ## _PORT, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, a ); \
+ } while (0)
+
+/* STM32F1 doesn't have AF pull up, but also doesn't disconnect af inputs so just use regular pull up */
+#define MAP_AF_PU(a) do { \
+ gpio_set_mode( a ## _PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, a); \
+ gpio_set( a ## _PORT, a); \
+ } while (0)
+
+#define MAP_AF_OD(a) do { \
+ gpio_set_mode( a ## _PORT, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, a ); \
+ } while (0)
+
+
+#define MAP_OUTPUT_PP(a) do { \
+ gpio_set_mode( a ## _PORT, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, a ); \
+ } while (0)
+
+
+#define MAP_OUTPUT_OD(a) do { \
+ gpio_set_mode( a ## _PORT, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, a ); \
+ } while (0)
+
+
+/* STM32F1 madly uses the output register to drive the other end of the resistor, so pull up */
+/* requires us to write a 1 there */
+
+#define MAP_INPUT_PU(a) do { \
+ gpio_set_mode( a ## _PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, a); \
+ gpio_set( a ## _PORT, a); \
+ } while (0)
+
+
+#define MAP_INPUT(a) do { \
+ gpio_set_mode( a ## _PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, a); \
+ } while (0)
+
+#define MAP_ANALOG(a) do { \
+ gpio_set_mode( a ## _PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_ANALOG, a); \
+ } while (0)
+
+
+#define CLEAR(a) gpio_clear( a ## _PORT, a)
+#define SET(a) gpio_set( a ## _PORT, a)
+#define GET(a) gpio_get( a ## _PORT, a)
+
+#endif
diff --git a/boiler-monster/stm32/app/pressure.c b/boiler-monster/stm32/app/pressure.c
new file mode 100644
index 0000000..f719e4d
--- /dev/null
+++ b/boiler-monster/stm32/app/pressure.c
@@ -0,0 +1,164 @@
+#include "project.h"
+
+#define PRESSURE GPIO0
+#define PRESSURE_PORT GPIOA
+#define PRESSURE_CHANNEL ADC_CHANNEL0
+#define VREF_CHANNEL ADC_CHANNEL17
+
+#define N_SAMPLES 1000
+
+#define T do { printf("%s:%d\r\n",__FILE__,__LINE__); } while (0)
+
+static int pressure;
+
+struct avg {
+ int acc;
+ unsigned n;
+};
+
+static void avg_reset(struct avg *a)
+{
+a->acc=0;
+a->n=0;
+return;
+}
+static void avg_add(struct avg *a, int v)
+{
+a->acc+=v;
+a->n++;
+}
+inline static unsigned avg_n(struct avg *a)
+{
+return a->n;
+}
+
+static int avg_get(struct avg *a)
+{
+if (!a->n) return 0;
+return (a->acc) / (a->n);
+}
+
+
+uint16_t pressure_ch (void)
+{
+ return pressure;
+}
+
+
+static void pressure_calculate (int v,int r)
+{
+ /* r is 1.25 volts, transducer is 0.5V -> 0 psi 4.5V -> 100psi */
+ /* 100psi is 6.8947573 bar, and we want 256ths of bar */
+
+
+ if (!r) {
+ pressure = 0;
+ // return;
+ } else {
+ pressure = ((v * 552) / r) - 221;
+
+ if (pressure < 0) pressure = 0;
+ }
+
+ printf ("QP: %d %d %d\r\n", v, r, (pressure * 100) / 256);
+}
+
+
+
+void pressure_tick(void)
+{
+static unsigned state;
+static unsigned backoff=MS_TO_TICKS(1000);
+static unsigned timeout;
+
+static struct avg vref_avg;
+static struct avg pressure_avg;
+
+
+timeout++;
+
+if (backoff) {
+ backoff--;
+ return;
+}
+
+
+switch (state) {
+case 0:
+ adc_off (ADC1);
+ adc_power_on (ADC1);
+ backoff=MS_TO_TICKS(5);
+ state++;
+ break;
+case 1:
+ ADC_CR2 (ADC1) |= ADC_CR2_RSTCAL;
+ backoff=MS_TO_TICKS(2);
+ state++;
+ break;
+case 2:
+ if (ADC_CR2(ADC1) & ADC_CR2_CAL)
+ state=0;
+ ADC_CR2 (ADC1) |= ADC_CR2_CAL;
+ backoff=MS_TO_TICKS(2);
+ state++;
+ break;
+case 3:
+ if (ADC_CR2(ADC1) & ADC_CR2_CAL)
+ state=0;
+ state++;
+
+ avg_reset(&vref_avg);
+ avg_reset(&pressure_avg);
+ break;
+case 4:
+ if (adc_convert_start(VREF_CHANNEL))
+ state=0;
+ else {
+ timeout=0;
+ state++;
+ }
+ break;
+case 5:
+ if (adc_convert_done()) {
+ avg_add(&vref_avg,adc_convert_get());
+ state++;
+ } else if (timeout > MS_TO_TICKS(5))
+ state=0;
+ break;
+case 6:
+ if (adc_convert_start(PRESSURE_CHANNEL))
+ state=0;
+ else {
+ timeout=0;
+ state++;
+ }
+ break;
+case 7:
+ if (adc_convert_done()) {
+ avg_add(&pressure_avg,adc_convert_get());
+
+ if (avg_n(&pressure_avg)<N_SAMPLES)
+ state=4;
+ else
+ state++;
+ } else if (timeout > MS_TO_TICKS(5))
+ state=0;
+ break;
+case 8:
+ pressure_calculate(avg_get(&pressure_avg),avg_get(&vref_avg));
+ state=0;
+ break;
+}
+
+}
+
+
+
+void pressure_init (void)
+{
+ MAP_ANALOG (PRESSURE);
+}
+
+
+
+
diff --git a/boiler-monster/stm32/app/project.h b/boiler-monster/stm32/app/project.h
new file mode 100644
index 0000000..d1bfa52
--- /dev/null
+++ b/boiler-monster/stm32/app/project.h
@@ -0,0 +1,30 @@
+#include <stdlib.h>
+#include <libopencm3/stm32/rcc.h>
+#include <libopencm3/stm32/gpio.h>
+#include <libopencm3/stm32/usart.h>
+#include <libopencm3/stm32/usb.h>
+#include <libopencm3/stm32/exti.h>
+#include <libopencm3/stm32/adc.h>
+#include <libopencm3/cm3/systick.h>
+#include <libopencm3/cm3/nvic.h>
+#include <libopencm3/cm3/cortex.h>
+#include <libopencm3/cm3/scb.h>
+#include <libopencm3/cm3/dwt.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include "ring.h"
+#include "pins.h"
+#include "1wire.h"
+
+
+#define US (72)
+#define MS (US * 1000)
+#define HZ (MS * 1000)
+
+#define MS_TO_TICKS(a) ((a) *2)
+
+
+#include "prototypes.h"
diff --git a/boiler-monster/stm32/app/prototypes.h b/boiler-monster/stm32/app/prototypes.h
new file mode 100644
index 0000000..5206734
--- /dev/null
+++ b/boiler-monster/stm32/app/prototypes.h
@@ -0,0 +1,106 @@
+/* main.c */
+extern void reset_hardware(void);
+extern int main(void);
+/* ot.c */
+extern unsigned ot_override_ch;
+extern unsigned ot_override_dhw;
+extern void ot_rx_thm(uint8_t *msg, int error);
+extern void ot_rx_blr(uint8_t *msg, int error);
+extern void ot_request_usr(unsigned type, unsigned id, uint8_t *data);
+extern void ot_tick(void);
+extern void ot_init(void);
+/* ot_phy_rx.c */
+extern void exti9_5_isr(void);
+extern void ot_phy_rx_tick(void);
+extern void ot_phy_rx_init(void);
+/* ot_phy_tx.c */
+extern void ot_phy_tx_tick(void);
+extern int ot_tx_thm(uint8_t *data);
+extern int ot_tx_blr(uint8_t *data);
+extern void ot_phy_tx_init(void);
+/* led.c */
+extern void led_red_set(int i);
+extern void led_green1_set(int i);
+extern void led_green2_set(int i);
+extern void led_blink(void);
+extern void led_yellow_set(int i);
+extern void led_tick(void);
+extern void led_init(void);
+/* ticker.c */
+extern void delay_us(uint32_t d);
+extern int tick_dispatch;
+extern void sys_tick_handler(void);
+extern void delay_ms(uint32_t d);
+extern void ticker_init(void);
+/* usart.c */
+extern ring_t rx1_ring;
+extern ring_t tx1_ring;
+extern ring_t rx3_ring;
+extern ring_t tx3_ring;
+extern void usart1_isr(void);
+extern void usart1_queue(uint8_t d);
+extern void usart1_drain(void);
+extern int usart1_write(char *ptr, int len, int blocking);
+extern void usart3_isr(void);
+extern void usart3_queue(uint8_t d);
+extern void usart3_drain(void);
+extern int usart3_write(char *ptr, int len, int blocking);
+extern void usart_init(void);
+/* ring.c */
+extern void ring_init(volatile ring_t *r, uint8_t *buf, size_t len);
+extern int ring_write_byte(volatile ring_t *r, uint8_t c);
+extern int ring_read_byte(volatile ring_t *r, uint8_t *c);
+extern int ring_write(volatile ring_t *r, uint8_t *buf, size_t len, int blocking);
+extern int ring_empty(volatile ring_t *r);
+/* stdio.c */
+extern unsigned block_stdio;
+extern int _open(const char *name, int flags, int mode);
+extern int _close(int file);
+extern int _write(int file, char *buf, int nbytes);
+extern int _read(int file, char *buf, int nbytes);
+extern int _lseek(int file, int offset, int whence);
+extern int isatty(int file);
+extern void stdio_drain(void);
+/* util.c */
+extern char int_to_hex_char(int a);
+/* commit.c */
+extern char scm_version[];
+/* cmd.c */
+extern void cmd_usart_dispatch(void);
+/* pic.c */
+extern void pic_passthru(void);
+extern void pic_init(void);
+/* 1wire.c */
+extern void onewire_tick(void);
+extern int onewire_reset(void);
+extern void onewire_write_byte(uint8_t v);
+extern uint8_t onewire_read_byte(void);
+extern void onewire_read_bytes(uint8_t *buf, int n);
+extern void onewire_write_bytes(const uint8_t *buf, int n);
+extern int onewire_select(const Onewire_addr *a);
+extern int onewire_reset_and_select(const Onewire_addr *a);
+extern int onewire_wait_complete(unsigned timeout);
+extern int onewire_check_crc(uint8_t *buf, int n, uint8_t v);
+extern int onewire_search(void);
+extern void onewire_init(void);
+/* temp.c */
+extern void temp_tick(void);
+extern void temp_dispatch(void);
+extern uint16_t temp_ch_return(void);
+extern uint16_t temp_supply_inlet(void);
+/* ds1820.c */
+extern int ds1820_read(const Onewire_addr *a, int *temp);
+/* pressure.c */
+extern uint16_t pressure_ch(void);
+extern void pressure_tick(void);
+extern void pressure_init(void);
+/* adc.c */
+extern void adc_dump(void);
+extern volatile unsigned timeout;
+extern void adc_tick(void);
+extern int adc_calibrate(void);
+extern int adc_convert_start(unsigned channel);
+extern int adc_convert_done(void);
+extern unsigned adc_convert_get(void);
+extern unsigned adc_convert(unsigned channel);
+extern void adc_init(void);
diff --git a/boiler-monster/stm32/app/ring.c b/boiler-monster/stm32/app/ring.c
new file mode 100644
index 0000000..973f345
--- /dev/null
+++ b/boiler-monster/stm32/app/ring.c
@@ -0,0 +1,77 @@
+#include "project.h"
+
+
+static inline size_t
+ring_next (volatile ring_t *r, size_t p)
+{
+ p++;
+
+ if (p >= r->size)
+ p -= r->size;
+
+ return p;
+}
+
+void
+ring_init (volatile ring_t *r, uint8_t *buf, size_t len)
+{
+ r->data = buf;
+ r->size = len;
+ r->write = 0;
+ r->read = 0;
+}
+
+int
+ring_write_byte (volatile ring_t *r, uint8_t c)
+{
+ size_t n = ring_next (r, r->write);
+
+ if (n == r->read)
+ return -EAGAIN;
+
+ r->data[r->write] = c;
+
+ r->write = n;
+
+ return 0;
+}
+
+
+int
+ring_read_byte (volatile ring_t *r, uint8_t *c)
+{
+ size_t n = ring_next (r, r->read);
+
+ if (r->read == r->write)
+ return -EAGAIN;
+
+ *c = r->data[r->read];
+ r->read = n;
+
+ return 0;
+}
+
+int
+ring_write (volatile ring_t *r, uint8_t *buf, size_t len, int blocking)
+{
+ while (len--) {
+ if (blocking) {
+ while (ring_write_byte (r, *buf));
+
+ buf++;
+ } else {
+ if (ring_write_byte (r, * (buf++)))
+ return -EAGAIN;
+ }
+ }
+
+ return 0;
+}
+
+
+
+int
+ring_empty (volatile ring_t *r)
+{
+ return (r->read == r->write) ? 1 : 0;
+}
diff --git a/boiler-monster/stm32/app/ring.h b/boiler-monster/stm32/app/ring.h
new file mode 100644
index 0000000..a329354
--- /dev/null
+++ b/boiler-monster/stm32/app/ring.h
@@ -0,0 +1,13 @@
+#ifndef _RING_H_
+#define _RING_H_
+
+typedef struct ring {
+ uint8_t *data;
+ size_t size;
+ size_t write;
+ size_t read;
+} ring_t;
+
+#endif
+
+
diff --git a/boiler-monster/stm32/app/stdio.c b/boiler-monster/stm32/app/stdio.c
new file mode 100644
index 0000000..bcdd5c7
--- /dev/null
+++ b/boiler-monster/stm32/app/stdio.c
@@ -0,0 +1,77 @@
+#include "project.h"
+
+unsigned block_stdio;
+
+int
+_open (const char *name, int flags, int mode)
+{
+ errno = ENOSYS;
+ return -1; /* Always fails */
+
+} /* _open () */
+
+int
+_close (int file)
+{
+ errno = EBADF;
+ return -1; /* Always fails */
+
+} /* _close () */
+
+int
+_write (int file, char *buf, int nbytes)
+{
+
+ int ret = nbytes;
+
+ if (!block_stdio)
+ ret = usart1_write (buf, nbytes, 1);
+
+ if (ret < 0) {
+ errno = -ret;
+ return -1;
+ }
+
+ return ret;
+} /* _write () */
+
+
+int
+_read (int file, char *buf, int nbytes)
+{
+
+ errno = -EAGAIN;
+ return -1; /* EOF */
+
+} /* _read () */
+
+#if 0
+int
+_fstat (int file, struct stat *st)
+{
+ st->st_mode = S_IFCHR;
+ return 0;
+
+} /* _fstat () */
+#endif
+
+int
+_lseek (int file, int offset, int whence)
+{
+ return 0;
+
+} /* _lseek () */
+
+int
+isatty (int file)
+{
+ return 1;
+
+} /* _isatty () */
+
+
+void
+stdio_drain (void)
+{
+ usart1_drain();
+}
diff --git a/boiler-monster/stm32/app/temp.c b/boiler-monster/stm32/app/temp.c
new file mode 100644
index 0000000..4548d01
--- /dev/null
+++ b/boiler-monster/stm32/app/temp.c
@@ -0,0 +1,76 @@
+#include "project.h"
+
+
+#define N_SENSORS 2
+
+#define SENSOR_INDEX_CH_RETURN 0
+#define SENSOR_INDEX_SUPPLY_INLET 1
+
+static const Onewire_addr s_addr[N_SENSORS] = {
+ [0] = {{0x28, 0x60, 0x06, 0x53, 0x03, 0x00, 0x00, 0xf5}},
+ [1] = {{0x28, 0xa4, 0x08, 0x53, 0x03, 0x00, 0x00, 0x14}},
+};
+
+
+static int s_temp[N_SENSORS];
+
+static unsigned poke;
+
+
+void temp_tick (void)
+{
+ static unsigned ticker;
+
+ ticker++;
+
+ if (ticker < 3000)
+ return;
+
+ ticker = 0;
+ poke = 1;
+
+}
+
+
+
+
+
+void temp_dispatch (void)
+{
+ static unsigned sensor;
+
+ if (!poke) return;
+
+ poke = 0;
+
+ if (sensor < N_SENSORS) {
+ if (ds1820_read (&s_addr[sensor], &s_temp[sensor]))
+ s_temp[sensor] = 0;
+
+
+ printf ("Q1W: sensor %d temp %d\r\n", sensor, (s_temp[sensor] * 100) / 256);
+
+
+ sensor++;
+ } else {
+#if 0
+ onewire_search();
+#endif
+ }
+
+ if (sensor == N_SENSORS)
+ sensor = 0;
+
+
+}
+
+
+
+uint16_t temp_ch_return (void)
+{
+ return s_temp[SENSOR_INDEX_CH_RETURN];
+}
+uint16_t temp_supply_inlet (void)
+{
+ return s_temp[SENSOR_INDEX_SUPPLY_INLET];
+}
diff --git a/boiler-monster/stm32/app/ticker.c b/boiler-monster/stm32/app/ticker.c
new file mode 100644
index 0000000..9e268ca
--- /dev/null
+++ b/boiler-monster/stm32/app/ticker.c
@@ -0,0 +1,105 @@
+#include "project.h"
+
+
+static volatile uint32_t delay_hms_count;
+static volatile uint32_t ticks;
+static uint32_t scale = 7;
+
+void
+delay_us (uint32_t d)
+{
+ d *= scale;
+
+ while (d--)
+ __asm__ ("nop");
+}
+
+
+void
+sys_tick_handler (void)
+{
+ if (delay_hms_count)
+ delay_hms_count--;
+
+
+ led_tick();
+ ot_phy_tx_tick();
+ ot_tick();
+ onewire_tick();
+ temp_tick();
+ adc_tick();
+ pressure_tick();
+
+
+ ticks++;
+}
+
+
+
+void
+delay_ms (uint32_t d)
+{
+ delay_hms_count = d << 1;
+
+ while (delay_hms_count);
+}
+
+#if 0
+int
+timed_out (uint32_t then, unsigned int ms)
+{
+ then = ticks - then;
+
+ if (then > ms)
+ return 1;
+
+ return 0;
+}
+
+int
+timed_out_cycles (uint32_t then, unsigned int cycles)
+{
+ then = dwt_read_cycle_counter() - then;
+
+ if (then > cycles)
+ return 1;
+
+ return 0;
+}
+#endif
+
+
+
+void
+ticker_init (void)
+{
+ uint32_t v, w;
+
+ /*Start periodic timer */
+
+ systick_set_clocksource (STK_CSR_CLKSOURCE_AHB_DIV8);
+ /* 72MHz / 8 = > 9Mhz */
+ systick_set_reload (4500);
+ /* 9MHz / 4500 => 2kHz */
+ systick_interrupt_enable();
+ systick_counter_enable();
+
+ /*Calibrate the delay loop */
+
+
+
+ do {
+ scale--;
+ v = ticks;
+
+ while (v == ticks);
+
+ delay_us (500);
+ w = ticks;
+ v++;
+ w -= v;
+ } while (w);
+
+
+
+}
diff --git a/boiler-monster/stm32/app/usart.c b/boiler-monster/stm32/app/usart.c
new file mode 100644
index 0000000..28676a0
--- /dev/null
+++ b/boiler-monster/stm32/app/usart.c
@@ -0,0 +1,175 @@
+#include "project.h"
+
+#define BUFFER_SIZE 256
+
+
+#define USART1_TX GPIO_USART1_TX
+#define USART1_TX_PORT GPIOA
+
+#define USART1_RX GPIO_USART1_RX
+#define USART1_RX_PORT GPIOA
+
+#define USART3_TX GPIO_USART3_TX
+#define USART3_TX_PORT GPIOB
+
+#define USART3_RX GPIO_USART3_RX
+#define USART3_RX_PORT GPIOB
+
+
+
+ring_t rx1_ring;
+static uint8_t rx1_ring_buf[BUFFER_SIZE];
+
+ring_t tx1_ring;
+static uint8_t tx1_ring_buf[BUFFER_SIZE];
+
+ring_t rx3_ring;
+static uint8_t rx3_ring_buf[BUFFER_SIZE];
+
+ring_t tx3_ring;
+static uint8_t tx3_ring_buf[BUFFER_SIZE];
+
+void
+usart1_isr (void)
+{
+ uint8_t data;
+
+ /* Check if we were called because of RXNE. */
+ if (((USART_CR1 (USART1) & USART_CR1_RXNEIE) != 0) &&
+ ((USART_SR (USART1) & USART_SR_RXNE) != 0)) {
+ data = usart_recv (USART1);
+ ring_write_byte (&rx1_ring, data);
+ }
+
+ /* Check if we were called because of TXE. */
+ if (((USART_CR1 (USART1) & USART_CR1_TXEIE) != 0) &&
+ ((USART_SR (USART1) & USART_SR_TXE) != 0)) {
+
+ if (ring_read_byte (&tx1_ring, &data)) {
+ /*No more data, Disable the TXE interrupt, it's no longer needed. */
+ usart_disable_tx_interrupt (USART1);
+ } else
+ usart_send_blocking (USART1, data);
+ }
+
+}
+
+void
+usart1_queue (uint8_t d)
+{
+ ring_write_byte (&tx1_ring, d);
+ usart_enable_tx_interrupt (USART1);
+}
+
+void
+usart1_drain (void)
+{
+ while (!ring_empty (&tx1_ring));
+}
+
+
+int
+usart1_write (char *ptr, int len, int blocking)
+{
+ int ret;
+
+ ret = ring_write (&tx1_ring, (uint8_t *) ptr, len, blocking);
+ usart_enable_tx_interrupt (USART1);
+ return ret;
+}
+
+
+void
+usart3_isr (void)
+{
+ uint8_t data;
+
+ /* Check if we were called because of RXNE. */
+ if (((USART_CR1 (USART3) & USART_CR1_RXNEIE) != 0) &&
+ ((USART_SR (USART3) & USART_SR_RXNE) != 0)) {
+ data = usart_recv (USART3);
+ ring_write_byte (&rx3_ring, data);
+ }
+
+ /* Check if we were called because of TXE. */
+ if (((USART_CR1 (USART3) & USART_CR1_TXEIE) != 0) &&
+ ((USART_SR (USART3) & USART_SR_TXE) != 0)) {
+
+ if (ring_read_byte (&tx3_ring, &data)) {
+ /*No more data, Disable the TXE interrupt, it's no longer needed. */
+ usart_disable_tx_interrupt (USART3);
+ } else
+ usart_send_blocking (USART3, data);
+ }
+
+}
+
+void
+usart3_queue (uint8_t d)
+{
+ ring_write_byte (&tx3_ring, d);
+ usart_enable_tx_interrupt (USART3);
+}
+
+void
+usart3_drain (void)
+{
+ while (!ring_empty (&tx3_ring));
+}
+
+
+int
+usart3_write (char *ptr, int len, int blocking)
+{
+ int ret;
+
+ ret = ring_write (&tx3_ring, (uint8_t *) ptr, len, blocking);
+ usart_enable_tx_interrupt (USART3);
+ return ret;
+}
+
+
+void
+usart_init (void)
+{
+ ring_init (&rx1_ring, rx1_ring_buf, sizeof (rx1_ring_buf));
+ ring_init (&tx1_ring, tx1_ring_buf, sizeof (tx1_ring_buf));
+
+ nvic_enable_irq (NVIC_USART1_IRQ);
+
+ MAP_AF (USART1_TX);
+ MAP_AF_PU (USART1_RX);
+
+
+ usart_set_baudrate (USART1, 57600);
+ usart_set_databits (USART1, 8);
+ usart_set_stopbits (USART1, USART_STOPBITS_1);
+ usart_set_parity (USART1, USART_PARITY_NONE);
+ usart_set_flow_control (USART1, USART_FLOWCONTROL_NONE);
+ usart_set_mode (USART1, USART_MODE_TX_RX);
+
+ USART_CR1 (USART1) |= USART_CR1_RXNEIE;
+
+ usart_enable (USART1);
+
+
+ ring_init (&rx3_ring, rx3_ring_buf, sizeof (rx3_ring_buf));
+ ring_init (&tx3_ring, tx3_ring_buf, sizeof (tx3_ring_buf));
+
+
+ nvic_enable_irq (NVIC_USART3_IRQ);
+
+ MAP_AF (USART3_TX);
+ MAP_AF_PU (USART3_RX);
+
+ usart_set_baudrate (USART3, 9600);
+ usart_set_databits (USART3, 8);
+ usart_set_stopbits (USART3, USART_STOPBITS_1);
+ usart_set_parity (USART3, USART_PARITY_NONE);
+ usart_set_flow_control (USART3, USART_FLOWCONTROL_NONE);
+ usart_set_mode (USART3, USART_MODE_TX_RX);
+
+ USART_CR1 (USART3) |= USART_CR1_RXNEIE;
+
+ usart_enable (USART3);
+}
diff --git a/boiler-monster/stm32/app/util.c b/boiler-monster/stm32/app/util.c
new file mode 100644
index 0000000..bf65510
--- /dev/null
+++ b/boiler-monster/stm32/app/util.c
@@ -0,0 +1,14 @@
+#include "project.h"
+
+char int_to_hex_char (int a)
+{
+ if (a < 0) return '?';
+
+ if (a < 0xa) return '0' + a;
+
+ if (a < 0x10) return '7' + a;
+
+ return '?';
+}
+
+
diff --git a/boiler-monster/stm32/docs/pinout.txt b/boiler-monster/stm32/docs/pinout.txt
new file mode 100644
index 0000000..03abe35
--- /dev/null
+++ b/boiler-monster/stm32/docs/pinout.txt
@@ -0,0 +1,24 @@
+
+A0 <- pressure sensor
+A2 <-> one wire bus
+A11 -> yellow LED (OD)
+A12 -> green LED 1 (OD)
+A15 -> green LED 2 (OD)
+
+B1 -> PIC RESET
+B3 -> red LED (OD)
+B6 <- PIC B6 LED_C (from A0, OT from thermostat)
+B7 <- PIC B7 LED_D (from A1, OT from boiler)
+B8 -> PIC A6 GPIO_A (to A3, OT to boiler)
+B9 -> PIC A7 GPIO_B (to A4, OT to thermostat)
+B10 -> PIC RX
+B11 <- PIX TX
+
+C13 -> onboard LED (OD)
+
+A2 -> MR3020 TX
+A3 <- MR4020 RX
+RST <- MR3020 GPIO5
+
+
+
diff --git a/boiler-monster/stm32/docs/pm0056.pdf b/boiler-monster/stm32/docs/pm0056.pdf
new file mode 100644
index 0000000..659c9de
--- /dev/null
+++ b/boiler-monster/stm32/docs/pm0056.pdf
Binary files differ
diff --git a/boiler-monster/stm32/docs/rm0008.pdf b/boiler-monster/stm32/docs/rm0008.pdf
new file mode 100644
index 0000000..8f3d0e1
--- /dev/null
+++ b/boiler-monster/stm32/docs/rm0008.pdf
Binary files differ
diff --git a/boiler-monster/stm32/docs/stm32f103c8.pdf b/boiler-monster/stm32/docs/stm32f103c8.pdf
new file mode 100644
index 0000000..2723cee
--- /dev/null
+++ b/boiler-monster/stm32/docs/stm32f103c8.pdf
Binary files differ
diff --git a/boiler-monster/stm32/docs/stm32f103c8t6_pinout_voltage01.png b/boiler-monster/stm32/docs/stm32f103c8t6_pinout_voltage01.png
new file mode 100644
index 0000000..2c22a74
--- /dev/null
+++ b/boiler-monster/stm32/docs/stm32f103c8t6_pinout_voltage01.png
Binary files differ
diff --git a/boiler-monster/stm32/libopencm3 b/boiler-monster/stm32/libopencm3
new file mode 160000
+Subproject 5c73d601763dd140bff020be8d6d06f03c2bea7