From 9d87c925a9eaa4fc256be3173c14a20d1469472d Mon Sep 17 00:00:00 2001 From: fishsoupisgood Date: Wed, 9 Sep 2020 11:53:37 +0100 Subject: everything, mostly, working --- radiator-plc/DOCS/DS18B20.pdf | Bin 0 -> 392186 bytes radiator-plc/DOCS/plc_notes.txt | 93 +++++++ radiator-plc/DOCS/radiator-numbering.txt | 8 + radiator-plc/DOCS/valve_01213210.pdf | Bin 0 -> 341645 bytes radiator-plc/DOCS/valve_wiring.txt | 15 ++ radiator-plc/mt300n-v2/.gitignore | 1 + radiator-plc/mt300n-v2/Makefile | 6 + radiator-plc/mt300n-v2/usr/bin/mqtt_if.pl | 76 ++++++ radiator-plc/mt300n-v2/usr/bin/run_mqtt_if | 6 + radiator-plc/stm32/.gitignore | 15 ++ radiator-plc/stm32/Makefile | 8 + radiator-plc/stm32/Makefile.include | 44 ++++ radiator-plc/stm32/Makefile.rules | 261 ++++++++++++++++++++ radiator-plc/stm32/app/1wire.c | 384 +++++++++++++++++++++++++++++ radiator-plc/stm32/app/1wire.h | 9 + radiator-plc/stm32/app/Makefile | 59 +++++ radiator-plc/stm32/app/control.c | 114 +++++++++ radiator-plc/stm32/app/ds18b20.c | 128 ++++++++++ radiator-plc/stm32/app/gdb.script | 2 + radiator-plc/stm32/app/hexdump.c | 57 +++++ radiator-plc/stm32/app/inputs.c | 26 ++ radiator-plc/stm32/app/led.c | 53 ++++ radiator-plc/stm32/app/logic.c | 158 ++++++++++++ radiator-plc/stm32/app/main.c | 150 +++++++++++ radiator-plc/stm32/app/outputs.c | 47 ++++ radiator-plc/stm32/app/pins.h | 53 ++++ radiator-plc/stm32/app/project.h | 29 +++ radiator-plc/stm32/app/prototypes.h | 80 ++++++ radiator-plc/stm32/app/radiator-plc.ld | 38 +++ radiator-plc/stm32/app/rain.c | 100 ++++++++ radiator-plc/stm32/app/ring.c | 69 ++++++ radiator-plc/stm32/app/ring.h | 6 + radiator-plc/stm32/app/ticker.c | 97 ++++++++ radiator-plc/stm32/app/usart.c | 149 +++++++++++ radiator-plc/stm32/app/watchdog.c | 16 ++ radiator-plc/stm32/libopencm3 | 1 + 36 files changed, 2358 insertions(+) create mode 100644 radiator-plc/DOCS/DS18B20.pdf create mode 100644 radiator-plc/DOCS/plc_notes.txt create mode 100644 radiator-plc/DOCS/radiator-numbering.txt create mode 100644 radiator-plc/DOCS/valve_01213210.pdf create mode 100644 radiator-plc/DOCS/valve_wiring.txt create mode 100644 radiator-plc/mt300n-v2/.gitignore create mode 100644 radiator-plc/mt300n-v2/Makefile create mode 100755 radiator-plc/mt300n-v2/usr/bin/mqtt_if.pl create mode 100755 radiator-plc/mt300n-v2/usr/bin/run_mqtt_if create mode 100644 radiator-plc/stm32/.gitignore create mode 100644 radiator-plc/stm32/Makefile create mode 100644 radiator-plc/stm32/Makefile.include create mode 100644 radiator-plc/stm32/Makefile.rules create mode 100644 radiator-plc/stm32/app/1wire.c create mode 100644 radiator-plc/stm32/app/1wire.h create mode 100644 radiator-plc/stm32/app/Makefile create mode 100644 radiator-plc/stm32/app/control.c create mode 100644 radiator-plc/stm32/app/ds18b20.c create mode 100644 radiator-plc/stm32/app/gdb.script create mode 100644 radiator-plc/stm32/app/hexdump.c create mode 100644 radiator-plc/stm32/app/inputs.c create mode 100644 radiator-plc/stm32/app/led.c create mode 100644 radiator-plc/stm32/app/logic.c create mode 100644 radiator-plc/stm32/app/main.c create mode 100644 radiator-plc/stm32/app/outputs.c create mode 100644 radiator-plc/stm32/app/pins.h create mode 100644 radiator-plc/stm32/app/project.h create mode 100644 radiator-plc/stm32/app/prototypes.h create mode 100644 radiator-plc/stm32/app/radiator-plc.ld create mode 100644 radiator-plc/stm32/app/rain.c create mode 100644 radiator-plc/stm32/app/ring.c create mode 100644 radiator-plc/stm32/app/ring.h create mode 100644 radiator-plc/stm32/app/ticker.c create mode 100644 radiator-plc/stm32/app/usart.c create mode 100644 radiator-plc/stm32/app/watchdog.c create mode 160000 radiator-plc/stm32/libopencm3 (limited to 'radiator-plc') diff --git a/radiator-plc/DOCS/DS18B20.pdf b/radiator-plc/DOCS/DS18B20.pdf new file mode 100644 index 0000000..52dc259 Binary files /dev/null and b/radiator-plc/DOCS/DS18B20.pdf differ diff --git a/radiator-plc/DOCS/plc_notes.txt b/radiator-plc/DOCS/plc_notes.txt new file mode 100644 index 0000000..89ce2a2 --- /dev/null +++ b/radiator-plc/DOCS/plc_notes.txt @@ -0,0 +1,93 @@ + +STM32 pin assignments + +PA0 X4 +PA1 X3 +PA2 RS422 pin 4 +PA3 RS422 pin 1 +PA4 +PA5 +PA6 RS422 pin 3 +PA7 Via 472 to Run switch +PA8 Y1 +PA9 RS232 +PA10 RS232 +PA11 Y2 +PA12 Y3 +PA13 SWDIO +PA14 SWCLK +PA15 + +PB0 10k to ground +PB1 AD1 via condiitoning (conditioning removed (apart from zener) and with 10k pull up) +PB2 AD0 via conditioning +PB3 +PB4 +PB5 +PB6 +PB7 +PB8 +PB9 +PB10 +PB11 +PB12 +PB13 +PB14 +PB15 X2 + +PC0 +PC1 +PC2 Err LED +PC3 Run LED +PC4 X5 +PC5 +PC6 X1 +PC7 X0 +PC8 24V detect +PC9 Y0 +PC10 +PC11 +PC12 +PC13 tamper RTC +PC14 OSC32 (no sign of xtal) +PC15 OSC32 (no sign of xtal) + +PD0 Y4 +PD1 Y5 +PD2 Y6 +PD3 Y7 +PD4 +PD5 +PD6 +PD7 +PD8 +PD9 +PD10 X13 +PD11 X12 +PD12 X11 +PD13 X10 +PD14 X7 +PD15 X6 + +PE0 +PE1 +PE2 +PE3 +PE4 +PE5 +PE6 +PE7 +PE8 +PE9 +PE10 +PE11 +PE12 +PE13 +PE14 +PE15 + +VBAT +BOOT0 strapped by R16/R72 + + + diff --git a/radiator-plc/DOCS/radiator-numbering.txt b/radiator-plc/DOCS/radiator-numbering.txt new file mode 100644 index 0000000..dabb942 --- /dev/null +++ b/radiator-plc/DOCS/radiator-numbering.txt @@ -0,0 +1,8 @@ +0: small radiator next to fireplace +1: dd nearest road +2: dd nearest garden +3: hall +4: kitchen +5: music room +6: bathroom + diff --git a/radiator-plc/DOCS/valve_01213210.pdf b/radiator-plc/DOCS/valve_01213210.pdf new file mode 100644 index 0000000..3ad1c10 Binary files /dev/null and b/radiator-plc/DOCS/valve_01213210.pdf differ diff --git a/radiator-plc/DOCS/valve_wiring.txt b/radiator-plc/DOCS/valve_wiring.txt new file mode 100644 index 0000000..882aa63 --- /dev/null +++ b/radiator-plc/DOCS/valve_wiring.txt @@ -0,0 +1,15 @@ +Pin numbers on RJ45 and DB-9 are the same + +1 or/w 1 wire bus +2 or GND +3 g/w uSwitch (short to gound to indicate valve open) +4 bl 5V +5 bl/w 24V +6 g uSwitch +7 br/w coil - plc pulls to ground to open valve +8 br coil + +(doubling up is to make wirign in plc neater) + + + diff --git a/radiator-plc/mt300n-v2/.gitignore b/radiator-plc/mt300n-v2/.gitignore new file mode 100644 index 0000000..859afb1 --- /dev/null +++ b/radiator-plc/mt300n-v2/.gitignore @@ -0,0 +1 @@ +stamp diff --git a/radiator-plc/mt300n-v2/Makefile b/radiator-plc/mt300n-v2/Makefile new file mode 100644 index 0000000..cdf9d24 --- /dev/null +++ b/radiator-plc/mt300n-v2/Makefile @@ -0,0 +1,6 @@ +STUFF=$(shell find usr \! -type d -print) + +stamp: ${STUFF} + tar cf - ${STUFF} | ssh radiator-plc0 "cd / && tar xvpf -" + #tar cf - ${STUFF} | ssh radiator-plc1 "cd / && tar xvpf -" + touch $@ diff --git a/radiator-plc/mt300n-v2/usr/bin/mqtt_if.pl b/radiator-plc/mt300n-v2/usr/bin/mqtt_if.pl new file mode 100755 index 0000000..f81b6e0 --- /dev/null +++ b/radiator-plc/mt300n-v2/usr/bin/mqtt_if.pl @@ -0,0 +1,76 @@ +#!/usr/bin/env perl + +use strict; + +use Device::SerialPort; +use Net::MQTT::Simple; + +sub chomp_harder($) { + my $ret = shift; + my $irs = $/; + $/ = "\n"; + chomp $ret; + $/ = "\r"; + chomp $ret; + $/ = $irs; + return $ret; +} + +sub get_line($$) { + my ( $port, $mqtt ) = @_; + + my $ret = ""; + + while (1) { + my ( $count, $byte ) = $port->read(1); + $mqtt->tick(); + + if ( $count > 0 ) { + $ret .= $byte; + return chomp_harder($ret) if $byte eq "\n"; + } + else { + $ret = chomp_harder($ret); + return $ret; + } + } +} + +my $plc_port = "/dev/ttyS1"; +my $plc = new Device::SerialPort($plc_port) || die "can't open $plc_port\n"; + +$plc->baudrate(38400); +$plc->parity("none"); +$plc->databits(8); +$plc->stopbits(1); +$plc->stty_icanon(0); + +$plc->read_char_time(0); +$plc->read_const_time(1000); + +my $mqtt = Net::MQTT::Simple->new("10.32.139.1"); + +sub mqtt_msg($$) { + my ( $topic, $message ) = @_; + + $plc->write("MQTT $topic $message\r\n"); +} + +$mqtt->subscribe( 'cmnd/+/var1', \&mqtt_msg ); +$mqtt->subscribe( 'cmnd/+/var2', \&mqtt_msg ); +$mqtt->subscribe( 'cmnd/+/var3', \&mqtt_msg ); + +while (1) { + my $line = get_line( $plc, $mqtt ); + next unless length($line) > 0; + + if ( $line =~ /MQTT\s+([^\s]+)\s+(.+)/ ) { + $mqtt->publish( $1, $2 ); + } + + if ( $line =~ /MQTTR\s+([^\s]+)\s+(.+)/ ) { + $mqtt->retain( $1, $2 ); + } + +} + diff --git a/radiator-plc/mt300n-v2/usr/bin/run_mqtt_if b/radiator-plc/mt300n-v2/usr/bin/run_mqtt_if new file mode 100755 index 0000000..2c06f6b --- /dev/null +++ b/radiator-plc/mt300n-v2/usr/bin/run_mqtt_if @@ -0,0 +1,6 @@ +#!/bin/sh + +while true; do + /usr/local/bin/mqtt_if.pl + sleep 1 +done diff --git a/radiator-plc/stm32/.gitignore b/radiator-plc/stm32/.gitignore new file mode 100644 index 0000000..64532f4 --- /dev/null +++ b/radiator-plc/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/radiator-plc/stm32/Makefile b/radiator-plc/stm32/Makefile new file mode 100644 index 0000000..e1cd497 --- /dev/null +++ b/radiator-plc/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/radiator-plc/stm32/Makefile.include b/radiator-plc/stm32/Makefile.include new file mode 100644 index 0000000..4f5cbd9 --- /dev/null +++ b/radiator-plc/stm32/Makefile.include @@ -0,0 +1,44 @@ +## +## This file is part of the libopencm3 project. +## +## Copyright (C) 2009 Uwe Hermann +## Copyright (C) 2010 Piotr Esden-Tempski +## +## 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 . +## + +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/radiator-plc/stm32/Makefile.rules b/radiator-plc/stm32/Makefile.rules new file mode 100644 index 0000000..a723e6f --- /dev/null +++ b/radiator-plc/stm32/Makefile.rules @@ -0,0 +1,261 @@ +# +## This file is part of the libopencm3 project. +## +## Copyright (C) 2009 Uwe Hermann +## Copyright (C) 2010 Piotr Esden-Tempski +## Copyright (C) 2013 Frantisek Burian +## +## 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 . +## + +# 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/radiator-plc/stm32/app/1wire.c b/radiator-plc/stm32/app/1wire.c new file mode 100644 index 0000000..dd66e92 --- /dev/null +++ b/radiator-plc/stm32/app/1wire.c @@ -0,0 +1,384 @@ +#include "project.h" + +#define GPIO_PORT GPIOB +#define GPIO GPIO1 + +#define BIT_ZERO '0' +#define BIT_ONE '1' +#define BIT_UNKNOWN 'U' +#define BIT_CONFLICT 'C' + + +static inline void +low (void) +{ + CLEAR (GPIO); +} + +static inline void +hiz (void) +{ + SET (GPIO); +} + +static inline int +get (void) +{ + return ! !GET (GPIO); +} + +/*Returns 1 if timeout - waits ~100ms */ +static inline int +wait_hiz (void) +{ + unsigned int timeout = 10000; + hiz(); + + while (!get()) { + timeout--; + + if (! (timeout--)) + return 1; + + delay_us (10); + } + + return 0; +} + + +/* returns 1 if nothing on bus or error */ +int +onewire_reset (void) +{ + int ret; + cm_disable_interrupts(); + low(); + delay_us (600); + hiz(); + delay_us (65); + ret = get(); + + if (wait_hiz()) + ret = 1; + + delay_us (100); + cm_enable_interrupts(); + return ret; +} + +void +onewire_one (void) +{ + cm_disable_interrupts(); + low(); + delay_us (10); + hiz(); + delay_us (90); + cm_enable_interrupts(); +} + +void +onewire_zero (void) +{ + cm_disable_interrupts(); + low(); + delay_us (90); + hiz(); + delay_us (10); + cm_enable_interrupts(); +} + +int +onewire_read (void) +{ + int ret; + cm_disable_interrupts(); + low(); + delay_us (10); + hiz(); + delay_us (20); + ret = get(); + delay_us (70); + cm_enable_interrupts(); + return ret; +} + + +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 */ + return -1; + + else if (!r && !ir) { /*Both */ + bits[i] = BIT_CONFLICT; + onewire_zero(); + } + + break; + + case BIT_CONFLICT: + if (!r && !ir) /*Both */ + onewire_zero(); + else + return -1; + + break; + + case BIT_ZERO: + if (!r) + onewire_zero(); + else + return -1; + + break; + + case BIT_ONE: + if (!ir) + onewire_one(); + else + 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; + + + printf ("One wire search\n"); + //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 ("O: {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_OUTPUT_OD_SLOW (GPIO); + + hiz(); + delay_us (20); + printf ("hiz -> %d\n", get()); + + low(); + + delay_us (20); + printf ("low -> %d\n", get()); + + hiz(); + delay_us (20); + + +} diff --git a/radiator-plc/stm32/app/1wire.h b/radiator-plc/stm32/app/1wire.h new file mode 100644 index 0000000..edfc1b9 --- /dev/null +++ b/radiator-plc/stm32/app/1wire.h @@ -0,0 +1,9 @@ +#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/radiator-plc/stm32/app/Makefile b/radiator-plc/stm32/app/Makefile new file mode 100644 index 0000000..8ef06f7 --- /dev/null +++ b/radiator-plc/stm32/app/Makefile @@ -0,0 +1,59 @@ +## +## This file is part of the libopencm3 project. +## +## Copyright (C) 2009 Uwe Hermann +## +## 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 . +## + +CPROTO=cproto +PROG=radiator-plc + +D=radiator-plc0 + +V=1 +default: ${PROG}.elf + +CSRCS=hexdump.c main.c ring.c ticker.c usart.c watchdog.c led.c 1wire.c inputs.c outputs.c logic.c ds18b20.c + +HSRCS= 1wire.h project.h ring.h + +BINARY = ${PROG} +OBJS = ${CSRCS:%.c=%.o} + +${OBJS}: + +include ../Makefile.include + +INCLUDES += -I.. + +protos: ${CSRCS} + echo -n > prototypes.h + ${CPROTO} $(INCLUDES) $(DEFINES) -e -v ${CSRCS} > prototypes.h.tmp + mv -f prototypes.h.tmp prototypes.h + +flash: ${PROG}.hex + ssh ${D} flash_stm32 < ${PROG}.hex + +local_flash: ${PROG}.hex + $(Q)$(OOCD) -f $(OOCD_INTERFACE) \ + -f $(OOCD_BOARD) \ + -c "init" -c "reset init" \ + -c "flash write_image erase $<" \ + -c "reset" \ + -c "shutdown" $(NULL) + +tidy: + astyle -A3 -s2 --attach-extern-c -L -c -w -Y -m0 -f -p -H -U -k3 -xj -xd ${CSRCS} ${HSRCS} + diff --git a/radiator-plc/stm32/app/control.c b/radiator-plc/stm32/app/control.c new file mode 100644 index 0000000..f3a35c9 --- /dev/null +++ b/radiator-plc/stm32/app/control.c @@ -0,0 +1,114 @@ +#include "project.h" + + +static uint32_t target = 25000; +static uint32_t value = ~0U; + +#define SHORT 200 +#define LONG 1200 +#define REPEAT 400 + +#define HYST 75UL + + +static void +show_temp (char t, uint32_t v, int y) +{ + char buf[16]; + uint32_t i, f; + + i = v / 1000; + f = v / 100 - (i * 10); + + if (v == ~0U) + { + sprintf (buf, "%c:error ", t); + } + else + { + sprintf (buf, "%c:%2d.%1d%cC", t, (int) i, (int) f, 0xdf); + } + + lcd_write (buf, 0, y); +} + +void +control_update (void) +{ + + show_temp ('P', value, 0); + show_temp ('T', target, 1); + + if (value == ~0U) + relay_off (); + else if (value < (target - HYST)) + relay_on (); + else if (value > (target + HYST)) + relay_off (); + + +} + +void +control_tick (void) +{ + static int down, up, update; + + if (!gpio_get (GPIOB, GPIO4)) + down++; + else + down = 0; + + if (!gpio_get (GPIOB, GPIO6)) + up++; + else + up = 0; + + if ((down == SHORT) || (down == LONG)) + { + if (target > 100) + target -= 100; + if (down == LONG) + down = LONG - REPEAT; + control_update (); + } + + if ((up == SHORT) || (up == LONG)) + { + if (target < 99900) + target += 100; + if (up == LONG) + up = LONG - REPEAT; + control_update (); + } + + update++; + + if (update == 1000) + { + control_update (); + update = 0; + } +} + +void +control_report (uint32_t v) +{ + + value = v; + control_update (); +} + +void +control_init (void) +{ + + gpio_set_mode (GPIOB, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, + GPIO4 | GPIO6); + gpio_set_mode (GPIOB, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, + GPIO5); + gpio_set (GPIOB, GPIO4 | GPIO6); + gpio_clear (GPIOB, GPIO5); + + +} diff --git a/radiator-plc/stm32/app/ds18b20.c b/radiator-plc/stm32/app/ds18b20.c new file mode 100644 index 0000000..3bd63ed --- /dev/null +++ b/radiator-plc/stm32/app/ds18b20.c @@ -0,0 +1,128 @@ +#include "project.h" + +#define DS18B20_CONVERT 0x44 +#define DS18B20_WRITE_SCRATCHPAD 0x4e +#define DS18B20_READ_SCRATCHPAD 0xbe + + +#define TIMEOUT 150 + + +unsigned extract_leu16 (uint8_t *d) +{ + uint32_t u; + + u = (uint32_t) d[0] | (((uint32_t) d[1]) << 8); + return u; +} + + +int extract_les16 (uint8_t *d) +{ + uint32_t u; + u = extract_leu16 (d); + + if (u & 0x8000) u |= 0xffff0000; + + return (int) u; +} + + +/* do not call from interrupt context (ie ticker)*/ +int +ds18b20_read_sp (const Onewire_addr *a, uint8_t *buf, unsigned len) +{ + if (onewire_reset_and_select (a)) + return ~0U; + + onewire_write_byte (DS18B20_READ_SCRATCHPAD); + onewire_read_bytes (buf, len); + + + if ((len == 9) && onewire_check_crc (buf, 8, buf[8])) + return ~0U; + + return 0; +} + +int +ds18b20_write_sp (const Onewire_addr *a, uint8_t *buf, unsigned len) +{ + if (onewire_reset_and_select (a)) + return ~0U; + + onewire_write_byte (DS18B20_WRITE_SCRATCHPAD); + onewire_write_bytes (buf, len); + + return 0; +} + +int ds18b20_convert (const Onewire_addr *a, unsigned timeout) +{ + + if (onewire_reset_and_select (a)) + return ~0U; + + onewire_write_byte (DS18B20_CONVERT); + + if (timeout && onewire_wait_complete (timeout)) + return ~0U; + + return 0; +} + +int +ds18b20_set_12 (const Onewire_addr *a) +{ + uint8_t buf[9]; + + + if (ds18b20_read_sp (a, buf, 9)) + return 1; + + buf[0] = 0x0; + buf[1] = 0x0; + buf[2] = 0x7f; /* 12 bit conversions */ + + return ds18b20_write_sp (a, buf, 3); + +} + +int ds18b20_read (const Onewire_addr *a, int *temp) +{ + uint8_t buf[9]; + + if (ds18b20_read_sp (a, buf, 9)) + return 1; + + *temp = (extract_les16 (buf) * 500) >> 3; + return 0; +} + + +int +ds18b20_set_12_convert (const Onewire_addr *a) +{ + + if (ds18b20_set_12 (a)) + return 1; + + if (ds18b20_convert (a, 0)) + return 1; + + return 0; +} + +/* do not call from interrupt context (ie ticker)*/ +int +ds18b20_set_12_convert_and_read (const Onewire_addr *a, int *temp) +{ + if (ds18b20_set_12 (a)) + return 1; + + if (ds18b20_convert (a, TIMEOUT)) + return 1; + + return ds18b20_read (a, temp); +} + diff --git a/radiator-plc/stm32/app/gdb.script b/radiator-plc/stm32/app/gdb.script new file mode 100644 index 0000000..69f0239 --- /dev/null +++ b/radiator-plc/stm32/app/gdb.script @@ -0,0 +1,2 @@ +target remote boreas:3333 +cont diff --git a/radiator-plc/stm32/app/hexdump.c b/radiator-plc/stm32/app/hexdump.c new file mode 100644 index 0000000..83f02ea --- /dev/null +++ b/radiator-plc/stm32/app/hexdump.c @@ -0,0 +1,57 @@ +#include "project.h" + +void +hexdump (void *_d, int len) +{ + uint8_t *d = (uint8_t *) _d; + int i, j, k; + int e; + + if (!d || len < 0) + return; + + e = len + 15; + e &= ~15; + + for (i = 0; i < e; i += 16) { + usart1_drain(); + printf (" %05x:", i); + + for (j = 0; j < 16; ++j) { + k = i + j; + + if (k < len) + printf (" %02x", d[k]); + else + printf (" "); + + if (j == 7) + printf (" "); + } + + printf (" "); + + for (j = 0; j < 16; ++j) { + k = i + j; + + if (k < len) { + uint8_t c = d[k]; + + if (c < 33) + c = '.'; + + if (c > 126) + c = '.'; + + printf ("%c", c); + } + + if (j == 7) + printf (" "); + } + + printf ("\r\n"); + } + + usart1_drain(); +} diff --git a/radiator-plc/stm32/app/inputs.c b/radiator-plc/stm32/app/inputs.c new file mode 100644 index 0000000..825a3fe --- /dev/null +++ b/radiator-plc/stm32/app/inputs.c @@ -0,0 +1,26 @@ +#include "project.h" + +#define N_INPUTS 14 + +static const uint32_t input_bank[N_INPUTS] = { GPIOC, GPIOC, GPIOB, GPIOA, GPIOA, GPIOC, GPIOD, GPIOD, 0, 0, GPIOD, GPIOD, GPIOD, GPIOD}; +static const uint32_t input[N_INPUTS] = { GPIO7, GPIO6, GPIO15, GPIO1, GPIO0, GPIO4, GPIO15, GPIO14, 0, 0, GPIO13, GPIO12, GPIO11, GPIO10}; + +int input_get (unsigned r) +{ + if (r >= N_INPUTS) return 0; + + if (!input[r]) return 0; + + return !gpio_get (input_bank[r], input[r]); +} + + +void inputs_init (void) +{ + unsigned r; + + for (r = 0; r < N_INPUTS; ++r) + if (input[r]) + gpio_set_mode (input_bank[r], GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, input[r]); + +} diff --git a/radiator-plc/stm32/app/led.c b/radiator-plc/stm32/app/led.c new file mode 100644 index 0000000..390ed2e --- /dev/null +++ b/radiator-plc/stm32/app/led.c @@ -0,0 +1,53 @@ +#include "project.h" + +#define LED0 GPIO3 +#define LED0_PORT GPIOC + +#define LED1 GPIO2 +#define LED1_PORT GPIOC + +static int led = 0; + +void +led_init (void) +{ + MAP_OUTPUT_PP (LED0); + MAP_OUTPUT_PP (LED1); + + SET (LED0); + SET (LED1); +} + +void led1_set (void) +{ + CLEAR (LED1); +} + +void led1_clear (void) +{ + SET (LED1); +} + + + +void +led_tick (void) +{ + static unsigned i; + + i++; + + if (i < 1000) return; + + i = 0; + + led ^= 1; + + if (!led) + CLEAR (LED0); + + if (led) + SET (LED0); + +} + diff --git a/radiator-plc/stm32/app/logic.c b/radiator-plc/stm32/app/logic.c new file mode 100644 index 0000000..8cab7ca --- /dev/null +++ b/radiator-plc/stm32/app/logic.c @@ -0,0 +1,158 @@ +#include "project.h" + + +#define N_VALVES 8 + +static volatile int semaphore = 0; + + + +static const char *valve_name[N_VALVES] = { + "dd_radiator1", + "dd_radiator2", + "dd_radiator3", + "hall_radiator", + "kitchen_radiator", + "music_room_radiator", + "bathroom_radiator", + "plc0_radiator7", +}; + +static Onewire_addr valve_owa[N_VALVES] = { + {{0x28, 0xe3, 0x5d, 0x56, 0xb5, 0x01, 0x3c, 0x96}}, + {{0x28, 0x0d, 0xaf, 0x56, 0xb5, 0x01, 0x3c, 0x89}}, + {{0x28, 0x79, 0xb1, 0x56, 0xb5, 0x01, 0x3c, 0x13}}, + {{0x28, 0x99, 0x8f, 0x56, 0xb5, 0x01, 0x3c, 0x1c}}, + {{0x28, 0xb4, 0x98, 0x6e, 0x32, 0x20, 0x01, 0xc7}}, + {{0x28, 0x0d, 0x4d, 0x56, 0xb5, 0x01, 0x3c, 0xd0}}, + {{0x28, 0xb1, 0x71, 0x75, 0x32, 0x20, 0x01, 0xa9}}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, +}; +volatile int temp[N_VALVES]; + +#define BAD_TEMP 0x7fffffff + +int low_limit[N_VALVES] = { + 15000, + 15000, + 15000, + 15000, + 15000, + 15000, + 15000, + 00000, +}; + + +int high_limit[N_VALVES] = { + 15000, + 15000, + 15000, + 15000, + 15000, + 15000, + 15000, + 00000, +}; + +int valve_state[N_VALVES]; + + + +void mqtt_dispatch (char *type, char *who, char *what, char *msg) +{ + unsigned i; + + if (strcmp (type, "cmnd")) return; + + for (i = 0; i < N_VALVES; ++i) { + + if (!strcmp (who, valve_name[i])) { + if (!strcmp (what, "var1")) + low_limit[i] = atoi (msg) * 1000; + + if (!strcmp (what, "var2")) + high_limit[i] = atoi (msg) * 1000; + } + } +} + + +static float scale(int i) +{ +float ret; + +ret=i; +return ret/1000.; +} + +void logic_tick (void) +{ + static unsigned i; + static int read_failed = 1; + static char msg_buf[80]; + int t; + int o; + int v; + + + + if (!read_failed) + read_failed = ds18b20_read (&valve_owa[i], &t); + + if (read_failed) { + temp[i] = BAD_TEMP; + valve_state[i] = 0; + } else { + temp[i] = t; + + if (t < low_limit[i]) + valve_state[i] = 1; + + if (t >= high_limit[i]) + valve_state[i] = 0; + } + + o = valve_state[i]; + + output_write (i, o); + v = input_get (i); + + usart1_queue_buf (msg_buf, sprintf (msg_buf, "\r\nMQTTR stat/%s/POWER %s\r\n", valve_name[i], o ? "ON" : "OFF")); + usart1_queue_buf (msg_buf, sprintf (msg_buf, "\r\nMQTTR stat/%s/OPEN %d\r\n", valve_name[i], v)); + + usart1_queue_buf (msg_buf, sprintf (msg_buf, "\r\nMQTTR stat/%s/var1 %.2f\r\n", valve_name[i], scale(low_limit[i]))); + + usart1_queue_buf (msg_buf, sprintf (msg_buf, "\r\nMQTTR stat/%s/var2 %.2f\r\n", valve_name[i], scale(high_limit[i]))); + + + + if (!read_failed) { + usart1_queue_buf (msg_buf, sprintf (msg_buf, "\r\nMQTTR stat/%s/TEMPERATURE %.2f\r\n", valve_name[i], scale(temp[i]))); + usart1_queue_buf (msg_buf, sprintf (msg_buf, "\r\nMQTTR stat/%s/DELTA %.2f\r\n", valve_name[i], scale(low_limit[i]-temp[i]))); + } + + + i++; + + if (i >= N_VALVES) + i = 0; + + if (request_search) { + request_search = 0; + onewire_search(); + } + + read_failed = ds18b20_set_12_convert (&valve_owa[i]); +} + + +void logic_slow_tick (void) +{ + semaphore = 1; +} + + +void logic_dispatch (void) +{ +} diff --git a/radiator-plc/stm32/app/main.c b/radiator-plc/stm32/app/main.c new file mode 100644 index 0000000..57b6752 --- /dev/null +++ b/radiator-plc/stm32/app/main.c @@ -0,0 +1,150 @@ +#include "project.h" + + +volatile int request_search; + +static void mqtt_cmd (char *type) +{ + char *m; + char *who, *what; + + m = rindex (type, ' '); + + if (!m) return; + + *m = 0; + m++; + + while (index (type, ' ') == type) + type++; + + who = index (type, ' '); + if (who) *who = 0; + + + who = index (type, '/'); + + if (!who) { + who = ""; + what = ""; + } else { + *who = 0; + who++; + + what = index (who, '/'); + + if (!what) + what = ""; + + else { + *what = 0; + what++; + } + } + + + mqtt_dispatch (type, who, what, m); +} + +static void cmd_dispatch (char *cmd) +{ + + if (!strcmp (cmd, "SEARCH")) { + request_search = 1; + return; + } + + if (!strncmp (cmd, "MQTT ", 5)) { + mqtt_cmd (cmd + 5); + return; + } + + + printf ("Unknown command %s\n", cmd); +} + + + + +static void +kbd_dispatch (void) +{ + static char cmd_buf[80]; + static unsigned cmd_ptr; + + uint8_t c; + + while (!ring_read_byte (&rx1_ring, &c)) { + + switch (c) { + case ' ': + case '\t': + if (!cmd_ptr) + break; + + /*fall through */ + default: + cmd_buf[cmd_ptr++] = c; + cmd_buf[cmd_ptr] = 0; + + + if (cmd_ptr < (sizeof (cmd_buf) - 1)) + break; + + /*fall through*/ + case '\r': + case '\n': + if (!cmd_ptr) + break; + + cmd_dispatch (cmd_buf); + cmd_ptr = 0; + break; + } + } +} + + +int +main (void) +{ + /*set up pll */ + rcc_clock_setup_in_hse_8mhz_out_72mhz(); + + /*turn on clocks which aren't done elsewhere */ + rcc_periph_clock_enable (RCC_GPIOA); + rcc_periph_clock_enable (RCC_GPIOB); + rcc_periph_clock_enable (RCC_GPIOC); + rcc_periph_clock_enable (RCC_GPIOD); + rcc_periph_clock_enable (RCC_GPIOE); + rcc_periph_clock_enable (RCC_AFIO); + + //watchdog_init (); + + /*Adjust interrupt priorities so that uarts trump timer */ + 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, 0xff); + + ticker_init(); + usart_init(); + led_init(); + onewire_init(); + inputs_init(); + outputs_init(); + + printf ("D: RESET\n"); + + //onewire_search(); + + for (;;) { + if (!ring_empty (&rx1_ring)) + kbd_dispatch(); + + logic_dispatch(); + + } + + return 0; +} diff --git a/radiator-plc/stm32/app/outputs.c b/radiator-plc/stm32/app/outputs.c new file mode 100644 index 0000000..67d0519 --- /dev/null +++ b/radiator-plc/stm32/app/outputs.c @@ -0,0 +1,47 @@ +#include "project.h" + +#define N_OUTPUTS 8 + +static const uint32_t output_bank[N_OUTPUTS] = { GPIOC, GPIOA, GPIOA, GPIOA, GPIOD, GPIOD, GPIOD, GPIOD}; +static const uint32_t output[N_OUTPUTS] = { GPIO9, GPIO8, GPIO11, GPIO12, GPIO0, GPIO1, GPIO2, GPIO3}; + +void output_set (unsigned r) +{ + if (r >= N_OUTPUTS) return; + + gpio_set (output_bank[r], output[r]); +} + +void output_clear (unsigned r) +{ + if (r >= N_OUTPUTS) return; + + gpio_clear (output_bank[r], output[r]); +} + +void outputs_set (unsigned v) +{ + unsigned r; + + for (r = 0; r < N_OUTPUTS; r++, v >>= 1) { + if (v & 1) output_set (r); + else output_clear (r); + } +} + +void output_write (unsigned r, unsigned v) +{ + if (v) output_set (r); + else output_clear (r); +} + + + +void outputs_init (void) +{ + unsigned r; + + for (r = 0; r < N_OUTPUTS; ++r) + gpio_set_mode (output_bank[r], GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, output[r]); + +} diff --git a/radiator-plc/stm32/app/pins.h b/radiator-plc/stm32/app/pins.h new file mode 100644 index 0000000..657cbf9 --- /dev/null +++ b/radiator-plc/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) + +#define MAP_OUTPUT_OD_SLOW(a) do { \ + gpio_set_mode( a ## _PORT, GPIO_MODE_OUTPUT_2_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 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/radiator-plc/stm32/app/project.h b/radiator-plc/stm32/app/project.h new file mode 100644 index 0000000..85f14b9 --- /dev/null +++ b/radiator-plc/stm32/app/project.h @@ -0,0 +1,29 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +#include +#include + +#include "pins.h" +#include "ring.h" +#include "1wire.h" + +#include "prototypes.h" + +#define MEH do { printf("Z: %s:%d\r\n",__FUNCTION__,__LINE__); } while (0) + +#define CLOCK_HZ 72000000 + diff --git a/radiator-plc/stm32/app/prototypes.h b/radiator-plc/stm32/app/prototypes.h new file mode 100644 index 0000000..2347f01 --- /dev/null +++ b/radiator-plc/stm32/app/prototypes.h @@ -0,0 +1,80 @@ +/* hexdump.c */ +extern void hexdump(void *_d, int len); +/* main.c */ +extern volatile int request_search; +extern int main(void); +/* ring.c */ +extern void ring_init(ring_t *r, uint8_t *buf, size_t len); +extern int ring_write_byte(ring_t *r, uint8_t c); +extern int ring_read_byte(ring_t *r, uint8_t *c); +extern int ring_write(ring_t *r, uint8_t *buf, size_t len); +extern int ring_empty(ring_t *r); +/* ticker.c */ +extern volatile uint32_t ticks; +extern uint32_t clock_scale; +extern void delay_us(uint32_t d); +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 unsigned locked; +extern void usart_ticker(void); +extern void usart1_isr(void); +extern int _write(int file, char *ptr, int len); +extern void usart1_queue(uint8_t d); +extern void usart1_queue_buf(void *buf, size_t len); +extern void usart1_drain(void); +extern void usart_init(void); +/* watchdog.c */ +extern void watchdog_init(void); +extern void sausages(void); +/* led.c */ +extern void led_init(void); +extern void led1_set(void); +extern void led1_clear(void); +extern void led_tick(void); +/* 1wire.c */ +extern int onewire_reset(void); +extern void onewire_one(void); +extern void onewire_zero(void); +extern int onewire_read(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); +/* inputs.c */ +extern int input_get(unsigned r); +extern void inputs_init(void); +/* outputs.c */ +extern void output_set(unsigned r); +extern void output_clear(unsigned r); +extern void outputs_set(unsigned v); +extern void output_write(unsigned r, unsigned v); +extern void outputs_init(void); +/* logic.c */ +extern volatile int temp[8]; +extern int low_limit[8]; +extern int high_limit[8]; +extern int valve_state[8]; +extern void mqtt_dispatch(char *type, char *who, char *what, char *msg); +extern void logic_tick(void); +extern void logic_slow_tick(void); +extern void logic_dispatch(void); +/* ds18b20.c */ +extern unsigned extract_leu16(uint8_t *d); +extern int extract_les16(uint8_t *d); +extern int ds18b20_read_sp(const Onewire_addr *a, uint8_t *buf, unsigned len); +extern int ds18b20_write_sp(const Onewire_addr *a, uint8_t *buf, unsigned len); +extern int ds18b20_convert(const Onewire_addr *a, unsigned timeout); +extern int ds18b20_set_12(const Onewire_addr *a); +extern int ds18b20_read(const Onewire_addr *a, int *temp); +extern int ds18b20_set_12_convert(const Onewire_addr *a); +extern int ds18b20_set_12_convert_and_read(const Onewire_addr *a, int *temp); diff --git a/radiator-plc/stm32/app/radiator-plc.ld b/radiator-plc/stm32/app/radiator-plc.ld new file mode 100644 index 0000000..0fb2378 --- /dev/null +++ b/radiator-plc/stm32/app/radiator-plc.ld @@ -0,0 +1,38 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2009 Uwe Hermann + * + * 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 . + */ + +/* 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 + + + + +/* PROVIDE(_stack = dfu_shared_location ); */ + + + diff --git a/radiator-plc/stm32/app/rain.c b/radiator-plc/stm32/app/rain.c new file mode 100644 index 0000000..4188990 --- /dev/null +++ b/radiator-plc/stm32/app/rain.c @@ -0,0 +1,100 @@ +#include "project.h" + +#define DEBOUNCE 20 + +static uint8_t r_state; +static uint32_t r_ticks; +static uint8_t r_current; +static uint8_t r_filtered; +static unsigned r_expiry; + + +#define RAIN_BANK GPIOA +#define RAIN_GPIO GPIO15 + +static int +raw_rain (void) +{ + return !gpio_get (RAIN_BANK, RAIN_GPIO); +} + + +void rain_notify (void) +{ + if (r_current) + vents_rain_notify(); +} + + +void rain_ticker (void) +{ + unsigned s; + static int ticker = 0; + + + s = raw_rain(); + + if (s == r_state) { + if (r_ticks < DEBOUNCE) + r_ticks++; + else if (r_ticks == DEBOUNCE) { + r_current = s; + r_ticks++; + printf ("D: rain state now %d\r\n", r_current); + } + } else { + r_ticks = 0; + r_state = s; + } + + if (r_current) { + r_filtered = 1; + r_expiry = 1800; + } + + rain_notify(); + + ticker++; + + if (ticker < 1000) return; + + ticker = 0; + + if (r_expiry) + r_expiry--; + else + r_filtered = r_current; + +} + +void rain_slow_ticker (void) +{ + printf ("R: %d %d\r\n", r_current, r_filtered); +} + + +int rain_filtered_state (void) +{ + return r_filtered; +} + +int rain_state (void) +{ + return r_current; +} + +char rain_state_char (void) +{ + return r_current ? 'Y' : 'N'; +} + +void +rain_init (void) +{ + gpio_set_mode (RAIN_BANK, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, + RAIN_GPIO); + + gpio_set (RAIN_BANK, RAIN_GPIO); +} + + diff --git a/radiator-plc/stm32/app/ring.c b/radiator-plc/stm32/app/ring.c new file mode 100644 index 0000000..c1d1803 --- /dev/null +++ b/radiator-plc/stm32/app/ring.c @@ -0,0 +1,69 @@ +#include "project.h" + + +static inline size_t +ring_next (ring_t *r, size_t p) +{ + p++; + + if (p >= r->size) + p -= r->size; + + return p; +} + +void +ring_init (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 (ring_t *r, uint8_t c) +{ + size_t n = ring_next (r, r->write); + + if (n == r->read) + return -1; + + r->data[r->write] = c; + + r->write = n; + + return 0; +} + + +int +ring_read_byte (ring_t *r, uint8_t *c) +{ + size_t n = ring_next (r, r->read); + + if (r->read == r->write) + return -1; + + *c = r->data[r->read]; + r->read = n; + + return 0; +} + +int +ring_write (ring_t *r, uint8_t *buf, size_t len) +{ + while (len--) { + if (ring_write_byte (r, * (buf++))) + return -1; + } + + return 0; +} + +int +ring_empty (ring_t *r) +{ + return (r->read == r->write) ? 1 : 0; +} diff --git a/radiator-plc/stm32/app/ring.h b/radiator-plc/stm32/app/ring.h new file mode 100644 index 0000000..1eae65b --- /dev/null +++ b/radiator-plc/stm32/app/ring.h @@ -0,0 +1,6 @@ +typedef struct ring { + uint8_t *data; + size_t size; + size_t write; + size_t read; +} ring_t; diff --git a/radiator-plc/stm32/app/ticker.c b/radiator-plc/stm32/app/ticker.c new file mode 100644 index 0000000..bb346fb --- /dev/null +++ b/radiator-plc/stm32/app/ticker.c @@ -0,0 +1,97 @@ +#include "project.h" + +static volatile uint32_t delay_ms_count; +volatile uint32_t ticks; +static volatile int tick_ready; + +uint32_t clock_scale = 72; + +void +delay_us (uint32_t d) +{ + d *= clock_scale; + + while (d--) + __asm__ ("nop"); +} + + + +void +sys_tick_handler (void) +{ + static unsigned slow = 0; + static unsigned medium = 0; + + if (delay_ms_count) + delay_ms_count--; + + ticks++; + + if (!tick_ready) + return; + + //buttons_ticker(); + // + led_tick(); + usart_ticker(); + + + medium++; + + if (medium < 1000) return; + + medium = 0; + + //target_tick(); + + slow++; + + if (slow < 2) return; + logic_tick(); + + +} + + + +void +delay_ms (uint32_t d) +{ + delay_ms_count = d; + + while (delay_ms_count); +} + + +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 (9000); + /* 9MHz / 9000 => 1kHz */ + systick_interrupt_enable(); + systick_counter_enable(); + + do { + clock_scale--; + v = ticks; + + while (v == ticks); + + delay_us (1000); + w = ticks; + v++; + w -= v; + } while (w); + + + tick_ready = 1; + + SCS_DEMCR |= SCS_DEMCR_TRCENA; + SCS_DWT_CTRL |= SCS_DWT_CTRL_CYCCNTENA; +} diff --git a/radiator-plc/stm32/app/usart.c b/radiator-plc/stm32/app/usart.c new file mode 100644 index 0000000..52866b7 --- /dev/null +++ b/radiator-plc/stm32/app/usart.c @@ -0,0 +1,149 @@ +#include "project.h" + +#define BUFFER_SIZE 768 + +ring_t rx1_ring; +static uint8_t rx1_ring_buf[BUFFER_SIZE]; + +ring_t tx1_ring; +static uint8_t tx1_ring_buf[BUFFER_SIZE]; + +//unsigned locked = 25000; +unsigned locked = 0; + + + + + +void usart_ticker (void) +{ + if (locked) locked--; +} + + + +static char unlock[] = "octopus banana"; +static int unlock_ptr = 0; + + +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)) { + + /* Retrieve the data from the peripheral. */ + data = usart_recv (USART1); + + if (locked) { + if (unlock[unlock_ptr] == data) + unlock_ptr++; + else + unlock_ptr = 0; + + if (!unlock[unlock_ptr]) { + printf ("Unlocked!\r\n"); + unlock_ptr = 0; + locked = 0; + } + } + + + if (!locked) + 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_CR1 (USART1) &= ~USART_CR1_TXEIE; + } else + usart_send (USART1, data); + } + +} + + +int +_write (int file, char *ptr, int len) +{ + int ret = len; + + if (file == 1) { + while (len--) { + if (*ptr == '\n') + ring_write_byte (&tx1_ring, '\r'); + + ring_write_byte (&tx1_ring, * (ptr++)); + } + + USART_CR1 (USART1) |= USART_CR1_TXEIE; + return ret; + } + + errno = EIO; + return -1; +} + +void +usart1_queue (uint8_t d) +{ + ring_write_byte (&tx1_ring, d); + USART_CR1 (USART1) |= USART_CR1_TXEIE; +} +void +usart1_queue_buf (void *buf, size_t len) +{ + ring_write (&tx1_ring, buf, len); + USART_CR1 (USART1) |= USART_CR1_TXEIE; +} + + + +void +usart1_drain (void) +{ + while (!ring_empty (&tx1_ring)); +} + + + +void +usart_init (void) +{ + rcc_periph_clock_enable (RCC_USART1); + + ring_init (&rx1_ring, rx1_ring_buf, sizeof (rx1_ring_buf)); + ring_init (&tx1_ring, tx1_ring_buf, sizeof (tx1_ring_buf)); + + + /* Enable the USART1,2 interrupt. */ + nvic_enable_irq (NVIC_USART1_IRQ); + + /* Map pins, and set usart2 to have pull ups */ + gpio_set_mode (GPIOA, GPIO_MODE_OUTPUT_50_MHZ, + GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO_USART1_TX); + gpio_set_mode (GPIOA, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, + GPIO_USART1_RX); + + /* Setup UART1 parameters. */ + usart_set_baudrate (USART1, 38400); + 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); + + + /* Enable USART1 Receive interrupt. */ + USART_CR1 (USART1) |= USART_CR1_RXNEIE; + + /* Finally enable the USARTs. */ + usart_enable (USART1); +} diff --git a/radiator-plc/stm32/app/watchdog.c b/radiator-plc/stm32/app/watchdog.c new file mode 100644 index 0000000..8516119 --- /dev/null +++ b/radiator-plc/stm32/app/watchdog.c @@ -0,0 +1,16 @@ +#include "project.h" + + + +void +watchdog_init (void) +{ + //iwdg_set_period_ms (10000); + //iwdg_start(); +} + +void +sausages() +{ + iwdg_reset(); +} diff --git a/radiator-plc/stm32/libopencm3 b/radiator-plc/stm32/libopencm3 new file mode 160000 index 0000000..5c73d60 --- /dev/null +++ b/radiator-plc/stm32/libopencm3 @@ -0,0 +1 @@ +Subproject commit 5c73d601763dd140bff020be8d6d06f03c2bea78 -- cgit v1.2.3