diff --git a/F0:F030,F042,F072/TSYS_controller/Makefile b/F0:F030,F042,F072/TSYS_controller/Makefile
new file mode 100644
index 0000000..253bb3e
--- /dev/null
+++ b/F0:F030,F042,F072/TSYS_controller/Makefile
@@ -0,0 +1,10 @@
+BINARY := tsys01
+# MCU code
+MCU := F042x6
+# change this linking script depending on particular MCU model,
+LDSCRIPT := stm32f042x6.ld
+
+DEFINES := -DUSARTNUM=1 -DI2CPINS=67
+
+include ../makefile.f0
+include ../../makefile.stm32
diff --git a/F0:F030,F042,F072/TSYS_controller/Makefile_old b/F0:F030,F042,F072/TSYS_controller/Makefile_old
new file mode 100644
index 0000000..2081386
--- /dev/null
+++ b/F0:F030,F042,F072/TSYS_controller/Makefile_old
@@ -0,0 +1,158 @@
+BINARY = tsys01
+BOOTPORT ?= /dev/ttyUSB0
+BOOTSPEED ?= 57600
+# MCU FAMILY
+FAMILY = F0
+# MCU code
+MCU = F042x6
+# hardware definitions
+DEFS := -DUSARTNUM=1 -DI2CPINS=67
+#DEFS += -DCHECK_TMOUT
+#DEFS += -DEBUG
+# change this linking script depending on particular MCU model,
+# for example, if you have STM32F103VBT6, you should write:
+LDSCRIPT = stm32f042x6.ld
+
+# autoincremental version & build date
+VERSION_FILE := version.inc
+NEXTVER := $(shell expr $$(awk '/#define BUILD_NUMBER/' $(VERSION_FILE) | tr -cd "[0-9]") + 1)
+BUILDDATE := $(shell date +%Y-%m-%d)
+
+INDEPENDENT_HEADERS=
+
+FP_FLAGS ?= -msoft-float
+ASM_FLAGS = -mthumb -mcpu=cortex-m0 -march=armv6-m -mtune=cortex-m0
+ARCH_FLAGS = $(ASM_FLAGS) $(FP_FLAGS)
+
+###############################################################################
+# Executables
+OPREFIX ?= /opt/bin/arm-none-eabi
+#PREFIX ?= /usr/x86_64-pc-linux-gnu/arm-none-eabi/gcc-bin/7.3.0/arm-none-eabi
+PREFIX ?= $(OPREFIX)
+
+RM := rm -f
+RMDIR := rmdir
+CC := $(PREFIX)-gcc
+LD := $(PREFIX)-gcc
+AR := $(PREFIX)-ar
+AS := $(PREFIX)-as
+OBJCOPY := $(OPREFIX)-objcopy
+OBJDUMP := $(OPREFIX)-objdump
+GDB := $(OPREFIX)-gdb
+STFLASH := $(shell which st-flash)
+STBOOT := $(shell which stm32flash)
+DFUUTIL := $(shell which dfu-util)
+
+###############################################################################
+# Source files
+OBJDIR = mk
+LDSCRIPT ?= $(BINARY).ld
+SRC := $(wildcard *.c)
+OBJS := $(addprefix $(OBJDIR)/, $(SRC:%.c=%.o))
+STARTUP = $(OBJDIR)/startup.o
+OBJS += $(STARTUP)
+DEPS := $(OBJS:.o=.d)
+
+INC_DIR ?= ../inc
+
+INCLUDE := -I$(INC_DIR)/F0 -I$(INC_DIR)/cm
+LIB_DIR := $(INC_DIR)/ld
+
+###############################################################################
+# C flags
+CFLAGS += -O2 -g -D__thumb2__=1
+CFLAGS += -Wall -Werror -Wextra -Wshadow -Wimplicit-function-declaration
+CFLAGS += -Wredundant-decls $(INCLUDE)
+# -Wmissing-prototypes -Wstrict-prototypes
+CFLAGS += -fno-common -ffunction-sections -fdata-sections
+
+###############################################################################
+# Linker flags
+LDFLAGS += --static -nostartfiles
+#--specs=nano.specs
+LDFLAGS += -L$(LIB_DIR)
+LDFLAGS += -T$(LDSCRIPT)
+LDFLAGS += -Wl,-Map=$(OBJDIR)/$(BINARY).map
+LDFLAGS += -Wl,--gc-sections
+
+###############################################################################
+# Used libraries
+#LDLIBS += -Wl,--start-group -lc -lgcc -Wl,--end-group
+#LDLIBS += $(shell $(CC) $(CFLAGS) -print-libgcc-file-name)
+
+DEFS += -DSTM32$(FAMILY) -DSTM32$(MCU)
+
+#.SUFFIXES: .elf .bin .hex .srec .list .map .images
+#.SECONDEXPANSION:
+#.SECONDARY:
+
+ELF := $(OBJDIR)/$(BINARY).elf
+LIST := $(OBJDIR)/$(BINARY).list
+BIN := $(BINARY).bin
+HEX := $(BINARY).hex
+
+all: bin list
+
+elf: $(ELF)
+bin: $(BIN)
+hex: $(HEX)
+list: $(LIST)
+
+ifneq ($(MAKECMDGOALS),clean)
+-include $(DEPS)
+endif
+
+$(OBJDIR):
+ mkdir $(OBJDIR)
+
+$(STARTUP): $(INC_DIR)/startup/vector.c
+ $(CC) $(CFLAGS) $(DEFS) $(INCLUDE) $(ARCH_FLAGS) -o $@ -c $<
+
+$(VERSION_FILE): *.[ch]
+ @echo " Generate version: $(NEXTVER) for date $(BUILDDATE)"
+ @sed -i "s/#define BUILD_NUMBER.*/#define BUILD_NUMBER \"$(NEXTVER)\"/" $(VERSION_FILE)
+ @sed -i "s/#define BUILDNO.*/#define BUILDNO $(NEXTVER)/" $(VERSION_FILE)
+ @sed -i "s/#define BUILD_DATE.*/#define BUILD_DATE \"$(BUILDDATE)\"/" $(VERSION_FILE)
+
+$(OBJDIR)/proto.o: proto.c $(VERSION_FILE)
+
+$(OBJDIR)/%.o: %.c
+ @make $(VERSION_FILE)
+ @echo " CC $<"
+ $(CC) $(CFLAGS) -MD $(DEFS) $(INCLUDE) $(ARCH_FLAGS) -o $@ -c $<
+
+$(BIN): $(ELF)
+ @echo " OBJCOPY $(BIN)"
+ $(OBJCOPY) -Obinary $(ELF) $(BIN)
+
+$(HEX): $(ELF)
+ @echo " OBJCOPY $(HEX)"
+ $(OBJCOPY) -Oihex $(ELF) $(HEX)
+
+$(LIST): $(ELF)
+ @echo " OBJDUMP $(LIST)"
+ $(OBJDUMP) -S $(ELF) > $(LIST)
+
+$(ELF): $(OBJDIR) $(OBJS)
+ @echo " LD $(ELF)"
+ $(LD) $(LDFLAGS) $(ARCH_FLAGS) $(OBJS) $(LDLIBS) -o $(ELF)
+ @size $(ELF)
+
+clean:
+ @echo " CLEAN"
+ $(RM) $(OBJS) $(DEPS) $(ELF) $(HEX) $(LIST) $(OBJDIR)/*.map *.d
+ @rmdir $(OBJDIR) 2>/dev/null || true
+
+dfuboot: $(BIN)
+ @echo " LOAD $(BIN) THROUGH DFU"
+ $(DFUUTIL) -a0 -D $(BIN) -s 0x08000000
+
+flash: $(BIN)
+ @echo " FLASH $(BIN)"
+ $(STFLASH) write $(BIN) 0x8000000
+
+boot: $(BIN)
+ @echo " LOAD $(BIN) through bootloader"
+ $(STBOOT) -b$(BOOTSPEED) $(BOOTPORT) -w $(BIN)
+
+.PHONY: clean flash boot dfuboot
diff --git a/F0:F030,F042,F072/TSYS_controller/Readme.md b/F0:F030,F042,F072/TSYS_controller/Readme.md
new file mode 100644
index 0000000..2824aaa
--- /dev/null
+++ b/F0:F030,F042,F072/TSYS_controller/Readme.md
@@ -0,0 +1,133 @@
+# Firmware for controllers of thermal sensors
+
+Make regular scan of 8 sensors' pairs.
+USART speed 115200. Code for ../../kicad/stm32
+
+## Serial interface commands (ends with '\n'), small letter for only local processing:
+- **0...7** send message to Nth controller, not broadcast (after number should be CAN command)
+- **@** set/reset debug mode
+- **a** get raw ADC values
+- **B** send dummy CAN messages to broadcast address
+- **b** get/set CAN bus baudrate
+- **c** show coefficients for all thermosensors
+- **D** send dummy CAN messages to master (0) address
+- **d** get current CAN address of device
+- **Ee** end temperature scan
+- **Ff** turn sensors off
+- **g** group (sniffer) CAN mode (print to USB terminal all incoming CAN messages with alien IDs)
+- **Hh** switch I2C to high speed (100kHz)
+- **Ii** (re)init sensors
+- **Jj** get MCU temperature
+- **Kk** get values of U and I
+- **Ll** switch I2C to low speed (default, 10kHz)
+- **Mm** change master id to 0 (**m**) / broadcast (**M**)
+- **N** get build number
+- **Oo** turn onboard diagnostic LEDs **O**n or **o**ff (both commands are local!)
+- **P** ping everyone over CAN
+- **Qq** get system time
+- **Rr** reinit I2C
+- **s** send CAN message (format: ID data[0..8], dec, 0x - hex, 0b - binary)
+- **Tt** start single temperature measurement
+- **u** unique ID (default) CAN mode
+- **Vv** very low speed
+- **Xx** go into temperature scan mode
+- **Yy** get sensors state over CAN (data format: 3 - state, 4,5 - presense mask [0,1], 6 - npresent, 7 - ntempmeasured
+- **z** check CAN status for errors
+
+The command **M** allows to temporaly change master ID of all
+controllers to broadcast ID. So all data they sent will be
+accessed @ any controller.
+
+## PINOUT
+- **I2C**: PB6 (SCL) & PB7 (SDA)
+- **USART1**: PA9 (Tx) & PA10 (Rx)
+- **CAN bus**: PB8 (Rx), PB9 (Tx)
+- **USB bus**: PA11 (DM), PA12 (DP)
+- **I2C multiplexer**: PB0..PB2 (0..2 address bits), PB12 (~EN)
+- **sensors' power**: PB3 (in, overcurrent), PA8 (out, enable power)
+- **signal LEDs**: PB10 (LED0), PB11 (LED1)
+- **ADC inputs**: PA0 (V12/4.93), PA1 (V5/2), PA3 (I12 - 1V/A), PA6 (V3.3/2)
+- **controller CAN address**: PA13..PA15 (0..2 bits), PB15 (3rd bit); 0 - master, other address - slave
+
+
+## LEDS
+- LED0 (nearest to sensors' connectors) - heartbeat
+- LED1 (above LED0) - CAN bus OK
+
+## CAN protocol
+Variable data length: from 1 to 8 bytes.
+First (number zero) byte of every sequence is command mark (0xA5) or data mark (0x5A).
+
+## Commands
+### Common commands
+- `CMD_PING` (0) request for PONG cmd
+- `CMD_START_MEASUREMENT` (1) start single temperature measurement
+- `CMD_SENSORS_STATE` (2) get sensors state
+- `CMD_START_SCAN` (3) run scan mode
+- `CMD_STOP_SCAN` (4) stop scan mode
+- `CMD_SENSORS_OFF` (5) turn off power of sensors
+- `CMD_LOWEST_SPEED` (6) lowest I2C speed
+- `CMD_LOW_SPEED` (7) low I2C speed (10kHz)
+- `CMD_HIGH_SPEED` (8) high I2C speed (100kHz)
+- `CMD_REINIT_I2C` (9) reinit I2C with current speed
+- `CMD_CHANGE_MASTER_B` (10) change master id to broadcast
+- `CMD_CHANGE_MASTER` (11) change master id to 0
+- `CMD_GETMCUTEMP` (12) MCU temperature value
+- `CMD_GETUIVAL` (13) request to get values of V12, V5, I12 and V3.3
+- `CMD_GETUIVAL0` (14) answer with values of V12 and V5
+- `CMD_GETUIVAL1` (15) answer with values of I12 and V3.3
+- `CMD_REINIT_SENSORS` (16) (re)init all sensors (discover all and get calibrated data)
+- `CMD_GETBUILDNO` (17) get by CAN firmware build number (uint32_t, littleendian, starting from byte #4)
+- `CMD_SYSTIME` (18) get system time in ms (uint32_t, littleendian, starting from byte #4)
+
+### Dummy commands for test purposes
+- `CMD_DUMMY0` = 0xDA,
+- `CMD_DUMMY1` = 0xAD
+
+### Commands data format
+- byte 1 - Controller number
+- byte 2 - Command received
+- bytes 3..7 - data
+
+### Thermal data format
+- byte 3 - Sensor number (10*N + M, where N is multiplexer number, M - number of sensor in pair, i.e. 0,1,10,11,20,21...70,71)
+- byte 4 - thermal data H
+- byte 5 - thermal data L
+
+### Sensors state data format
+- byte 3 - Sstate value:
+ - `[SENS_INITING]` = "init"
+ - `[SENS_RESETING]` = "reset"
+ - `[SENS_GET_COEFFS]` = "getcoeff"
+ - `[SENS_SLEEPING]` = "sleep"
+ - `[SENS_START_MSRMNT]` = "startmeasure"
+ - `[SENS_WAITING]` = "waitresults"
+ - `[SENS_GATHERING]` = "collectdata"
+ - `[SENS_OFF]` = "off"
+ - `[SENS_OVERCURNT]` = "overcurrent"
+ - `[SENS_OVERCURNT_OFF]` = "offbyovercurrent"
+- byte 4 - `sens_present[0]` value
+- byte 5 - `sens_present[1]` value
+- byte 6 - `Nsens_present` value
+- byte 7 - `Ntemp_measured` value
+
+### MCU temperature data format
+- byte 3 - data H
+- byte 4 - data L
+
+All temperature is in degrC/100!
+
+### U and I data format
+- byte 2 - type of data (`CMD_GETUIVAL0` - V12 and V5, `CMD_GETUIVAL1` - I12 and V3.3)
+
+case CMD_GETUIVAL0
+
+- bytes 3,4 - V12 H/L
+- bytes 5,6 - V5 H/L
+
+case CMD_GETUIVAL1
+
+- bytes 3,4 - I12 H/L
+- bytes 5,6 - V33 H/L
+
+Voltage is in V/100, Current is in mA
diff --git a/F0:F030,F042,F072/TSYS_controller/TSYS_controller.cflags b/F0:F030,F042,F072/TSYS_controller/TSYS_controller.cflags
new file mode 100644
index 0000000..68d5165
--- /dev/null
+++ b/F0:F030,F042,F072/TSYS_controller/TSYS_controller.cflags
@@ -0,0 +1 @@
+-std=c17
\ No newline at end of file
diff --git a/F0:F030,F042,F072/TSYS_controller/TSYS_controller.config b/F0:F030,F042,F072/TSYS_controller/TSYS_controller.config
new file mode 100644
index 0000000..eebe71a
--- /dev/null
+++ b/F0:F030,F042,F072/TSYS_controller/TSYS_controller.config
@@ -0,0 +1,8 @@
+// Add predefined macros for your project here. For example:
+// #define THE_ANSWER 42
+#define FAMILY F0
+#define MCU F042x6
+#define STM32F0
+#define STM32F042x6
+#define USARTNUM 1
+#define I2CPINS 67
diff --git a/F0:F030,F042,F072/TSYS_controller/TSYS_controller.creator b/F0:F030,F042,F072/TSYS_controller/TSYS_controller.creator
new file mode 100644
index 0000000..e94cbbd
--- /dev/null
+++ b/F0:F030,F042,F072/TSYS_controller/TSYS_controller.creator
@@ -0,0 +1 @@
+[General]
diff --git a/F0:F030,F042,F072/TSYS_controller/TSYS_controller.creator.user b/F0:F030,F042,F072/TSYS_controller/TSYS_controller.creator.user
new file mode 100644
index 0000000..7ca48ef
--- /dev/null
+++ b/F0:F030,F042,F072/TSYS_controller/TSYS_controller.creator.user
@@ -0,0 +1,171 @@
+
+
+
+
+
+ EnvironmentId
+ {7bd84e39-ca37-46d3-be9d-99ebea85bc0d}
+
+
+ ProjectExplorer.Project.ActiveTarget
+ 0
+
+
+ ProjectExplorer.Project.EditorSettings
+
+ true
+ false
+ true
+
+ Cpp
+
+ CppGlobal
+
+
+
+ QmlJS
+
+ QmlJSGlobal
+
+
+ 2
+ KOI8-R
+ false
+ 4
+ false
+ 80
+ true
+ true
+ 1
+ false
+ true
+ false
+ 0
+ true
+ true
+ 0
+ 8
+ true
+ false
+ 1
+ true
+ false
+ true
+ *.md, *.MD, Makefile
+ false
+ true
+
+
+
+ ProjectExplorer.Project.PluginSettings
+
+
+ true
+ false
+ true
+ true
+ true
+ true
+
+
+ 0
+ true
+
+ true
+ true
+ Builtin.DefaultTidyAndClazy
+ 8
+
+
+
+ true
+
+
+
+
+ ProjectExplorer.Project.Target.0
+
+ Desktop
+ Desktop
+ Desktop
+ {65a14f9e-e008-4c1b-89df-4eaa4774b6e3}
+ 0
+ 0
+ 0
+
+ /Big/Data/00__Electronics/STM32/F0-nolib/TSYS_controller
+
+
+
+ all
+
+ true
+ GenericProjectManager.GenericMakeStep
+
+ 1
+ Сборка
+ Сборка
+ ProjectExplorer.BuildSteps.Build
+
+
+
+
+ clean
+
+ true
+ GenericProjectManager.GenericMakeStep
+
+ 1
+ Очистка
+ Очистка
+ ProjectExplorer.BuildSteps.Clean
+
+ 2
+ false
+
+ false
+
+ По умолчанию
+ GenericProjectManager.GenericBuildConfiguration
+
+ 1
+
+
+ 0
+ Развёртывание
+ Развёртывание
+ ProjectExplorer.BuildSteps.Deploy
+
+ 1
+
+ false
+ ProjectExplorer.DefaultDeployConfiguration
+
+ 1
+
+
+ 2
+
+ ProjectExplorer.CustomExecutableRunConfiguration
+
+ false
+ true
+ false
+ true
+
+ 1
+
+
+
+ ProjectExplorer.Project.TargetCount
+ 1
+
+
+ ProjectExplorer.Project.Updater.FileVersion
+ 22
+
+
+ Version
+ 22
+
+
diff --git a/F0:F030,F042,F072/TSYS_controller/TSYS_controller.creator.user.4.8-pre1 b/F0:F030,F042,F072/TSYS_controller/TSYS_controller.creator.user.4.8-pre1
new file mode 100644
index 0000000..87f66cb
--- /dev/null
+++ b/F0:F030,F042,F072/TSYS_controller/TSYS_controller.creator.user.4.8-pre1
@@ -0,0 +1,198 @@
+
+
+
+
+
+ EnvironmentId
+ {cf63021e-ef53-49b0-b03b-2f2570cdf3b6}
+
+
+ ProjectExplorer.Project.ActiveTarget
+ 0
+
+
+ ProjectExplorer.Project.EditorSettings
+
+ true
+ false
+ true
+
+ Cpp
+
+ CppGlobal
+
+
+
+ QmlJS
+
+ QmlJSGlobal
+
+
+ 2
+ KOI8-R
+ false
+ 4
+ false
+ 80
+ true
+ true
+ 1
+ false
+ false
+ 1
+ true
+ true
+ 0
+ 8
+ true
+ 2
+ true
+ true
+ true
+ true
+
+
+
+ ProjectExplorer.Project.PluginSettings
+
+
+
+ ProjectExplorer.Project.Target.0
+
+ Desktop
+ Desktop
+ {91347f2c-5221-46a7-80b1-0a054ca02f79}
+ 0
+ 0
+ 0
+
+ /home/eddy/Docs/SAO/BTA/MIRROR_CONTROL_termo/Project/STM32src/TSYS_controller
+
+
+
+ all
+
+ false
+
+
+ true
+ Сборка
+
+ GenericProjectManager.GenericMakeStep
+
+ 1
+ Сборка
+
+ ProjectExplorer.BuildSteps.Build
+
+
+
+
+ clean
+
+ false
+
+
+ true
+ Сборка
+
+ GenericProjectManager.GenericMakeStep
+
+ 1
+ Очистка
+
+ ProjectExplorer.BuildSteps.Clean
+
+ 2
+ false
+
+ По умолчанию
+ По умолчанию
+ GenericProjectManager.GenericBuildConfiguration
+
+ 1
+
+
+ 0
+ Установка
+
+ ProjectExplorer.BuildSteps.Deploy
+
+ 1
+ Конфигурация установки
+
+ ProjectExplorer.DefaultDeployConfiguration
+
+ 1
+
+
+ false
+ false
+ 1000
+
+ true
+
+ false
+ false
+ false
+ false
+ true
+ 0.01
+ 10
+ true
+ 1
+ 25
+
+ 1
+ true
+ false
+ true
+ valgrind
+
+ 0
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+ 10
+ 11
+ 12
+ 13
+ 14
+
+ 2
+
+
+
+
+
+
+ ProjectExplorer.CustomExecutableRunConfiguration
+ 3768
+ false
+ true
+ false
+ false
+ true
+
+ 1
+
+
+
+ ProjectExplorer.Project.TargetCount
+ 1
+
+
+ ProjectExplorer.Project.Updater.FileVersion
+ 18
+
+
+ Version
+ 18
+
+
diff --git a/F0:F030,F042,F072/TSYS_controller/TSYS_controller.creator.user.cf63021 b/F0:F030,F042,F072/TSYS_controller/TSYS_controller.creator.user.cf63021
new file mode 100644
index 0000000..04a03d0
--- /dev/null
+++ b/F0:F030,F042,F072/TSYS_controller/TSYS_controller.creator.user.cf63021
@@ -0,0 +1,160 @@
+
+
+
+
+
+ EnvironmentId
+ {cf63021e-ef53-49b0-b03b-2f2570cdf3b6}
+
+
+ ProjectExplorer.Project.ActiveTarget
+ 0
+
+
+ ProjectExplorer.Project.EditorSettings
+
+ true
+ false
+ true
+
+ Cpp
+
+ CppGlobal
+
+
+
+ QmlJS
+
+ QmlJSGlobal
+
+
+ 2
+ KOI8-R
+ false
+ 4
+ false
+ 80
+ true
+ true
+ 1
+ false
+ false
+ false
+ 1
+ true
+ true
+ 0
+ 8
+ true
+ false
+ 2
+ true
+ true
+ true
+ *.md, *.MD, Makefile
+ true
+ true
+
+
+
+ ProjectExplorer.Project.PluginSettings
+
+
+ true
+ true
+ Builtin.DefaultTidyAndClazy
+ 4
+
+
+
+ true
+
+
+
+
+ ProjectExplorer.Project.Target.0
+
+ Desktop
+ Desktop
+ Desktop
+ {91347f2c-5221-46a7-80b1-0a054ca02f79}
+ 0
+ 0
+ 0
+
+ /home/eddy/Docs/SAO/BTA/MIRROR_CONTROL_termo/Project/STM32src/TSYS_controller
+
+
+
+ all
+
+ true
+ GenericProjectManager.GenericMakeStep
+
+ 1
+ Build
+ Build
+ ProjectExplorer.BuildSteps.Build
+
+
+
+
+ clean
+
+ true
+ GenericProjectManager.GenericMakeStep
+
+ 1
+ Clean
+ Clean
+ ProjectExplorer.BuildSteps.Clean
+
+ 2
+ false
+
+ false
+
+ По умолчанию
+ GenericProjectManager.GenericBuildConfiguration
+
+ 1
+
+
+ 0
+ Deploy
+ Deploy
+ ProjectExplorer.BuildSteps.Deploy
+
+ 1
+
+ false
+ ProjectExplorer.DefaultDeployConfiguration
+
+ 1
+
+
+ 2
+
+ ProjectExplorer.CustomExecutableRunConfiguration
+
+ false
+ true
+ false
+ true
+
+ 1
+
+
+
+ ProjectExplorer.Project.TargetCount
+ 1
+
+
+ ProjectExplorer.Project.Updater.FileVersion
+ 22
+
+
+ Version
+ 22
+
+
diff --git a/F0:F030,F042,F072/TSYS_controller/TSYS_controller.cxxflags b/F0:F030,F042,F072/TSYS_controller/TSYS_controller.cxxflags
new file mode 100644
index 0000000..6435dfc
--- /dev/null
+++ b/F0:F030,F042,F072/TSYS_controller/TSYS_controller.cxxflags
@@ -0,0 +1 @@
+-std=c++17
\ No newline at end of file
diff --git a/F0:F030,F042,F072/TSYS_controller/TSYS_controller.files b/F0:F030,F042,F072/TSYS_controller/TSYS_controller.files
new file mode 100644
index 0000000..cb15fe1
--- /dev/null
+++ b/F0:F030,F042,F072/TSYS_controller/TSYS_controller.files
@@ -0,0 +1,23 @@
+adc.c
+adc.h
+usb.c
+usb.h
+usb_lib.h
+usb_lib.c
+usb_defs.h
+can.c
+can.h
+can_process.c
+can_process.h
+hardware.c
+hardware.h
+i2c.c
+i2c.h
+main.c
+proto.c
+proto.h
+sensors_manage.c
+sensors_manage.h
+usart.c
+usart.h
+version.inc
diff --git a/F0:F030,F042,F072/TSYS_controller/TSYS_controller.includes b/F0:F030,F042,F072/TSYS_controller/TSYS_controller.includes
new file mode 100644
index 0000000..3f4d0a1
--- /dev/null
+++ b/F0:F030,F042,F072/TSYS_controller/TSYS_controller.includes
@@ -0,0 +1,4 @@
+.
+../inc
+../inc/F0
+../inc/cm
diff --git a/F0:F030,F042,F072/TSYS_controller/adc.c b/F0:F030,F042,F072/TSYS_controller/adc.c
new file mode 100644
index 0000000..9d7ace9
--- /dev/null
+++ b/F0:F030,F042,F072/TSYS_controller/adc.c
@@ -0,0 +1,167 @@
+/*
+ * This file is part of the TSYS_controller project.
+ * Copyright 2019 Edward V. Emelianov .
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "adc.h"
+
+/**
+ * @brief ADC_array - array for ADC channels with median filtering:
+ * 0..3 - external channels
+ * 4 - internal Tsens
+ * 5 - Vref
+ */
+#define TSENS_CHAN (NUMBER_OF_ADC_CHANNELS-2)
+#define VREF_CHAN (NUMBER_OF_ADC_CHANNELS-1)
+static uint16_t ADC_array[NUMBER_OF_ADC_CHANNELS*9];
+
+/*
+ * ADC channels:
+ * IN0 - V12
+ * IN1 - V5
+ * IN3 - I12
+ * IN6 - V3.3
+ * IN16- temperature sensor
+ * IN17- vref
+ */
+void adc_setup(){
+ uint16_t ctr = 0; // 0xfff0 - more than 1.3ms
+ // Enable clocking
+ /* (1) Enable the peripheral clock of the ADC */
+ /* (2) Start HSI14 RC oscillator */
+ /* (3) Wait HSI14 is ready */
+ RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; /* (1) */
+ RCC->CR2 |= RCC_CR2_HSI14ON; /* (2) */
+ while ((RCC->CR2 & RCC_CR2_HSI14RDY) == 0 && ++ctr < 0xfff0){}; /* (3) */
+ // calibration
+ /* (1) Ensure that ADEN = 0 */
+ /* (2) Clear ADEN */
+ /* (3) Launch the calibration by setting ADCAL */
+ /* (4) Wait until ADCAL=0 */
+ if ((ADC1->CR & ADC_CR_ADEN) != 0){ /* (1) */
+ ADC1->CR &= (uint32_t)(~ADC_CR_ADEN); /* (2) */
+ }
+ ADC1->CR |= ADC_CR_ADCAL; /* (3) */
+ ctr = 0; // ADC calibration time is 5.9us
+ while ((ADC1->CR & ADC_CR_ADCAL) != 0 && ++ctr < 0xfff0){}; /* (4) */
+ // enable ADC
+ ctr = 0;
+ do{
+ ADC1->CR |= ADC_CR_ADEN;
+ }while ((ADC1->ISR & ADC_ISR_ADRDY) == 0 && ++ctr < 0xfff0);
+ // configure ADC
+ /* (1) Select HSI14 by writing 00 in CKMODE (reset value) */
+ /* (2) Select the continuous mode */
+ /* (3) Select CHSEL0,1,3,6 - ADC inputs, 16,17 - t. sensor and vref */
+ /* (4) Select a sampling mode of 111 i.e. 239.5 ADC clk to be greater than 17.1us */
+ /* (5) Wake-up the VREFINT and Temperature sensor (only for VBAT, Temp sensor and VRefInt) */
+ // ADC1->CFGR2 &= ~ADC_CFGR2_CKMODE; /* (1) */
+ ADC1->CFGR1 |= ADC_CFGR1_CONT; /* (2)*/
+ ADC1->CHSELR = ADC_CHSELR_CHSEL0 | ADC_CHSELR_CHSEL1 | ADC_CHSELR_CHSEL3 |
+ ADC_CHSELR_CHSEL6 | ADC_CHSELR_CHSEL16 | ADC_CHSELR_CHSEL17; /* (3)*/
+ ADC1->SMPR |= ADC_SMPR_SMP_0 | ADC_SMPR_SMP_1 | ADC_SMPR_SMP_2; /* (4) */
+ ADC->CCR |= ADC_CCR_TSEN | ADC_CCR_VREFEN; /* (5) */
+ // configure DMA for ADC
+ // DMA for AIN
+ /* (1) Enable the peripheral clock on DMA */
+ /* (2) Enable DMA transfer on ADC and circular mode */
+ /* (3) Configure the peripheral data register address */
+ /* (4) Configure the memory address */
+ /* (5) Configure the number of DMA tranfer to be performs on DMA channel 1 */
+ /* (6) Configure increment, size, interrupts and circular mode */
+ /* (7) Enable DMA Channel 1 */
+ RCC->AHBENR |= RCC_AHBENR_DMA1EN; /* (1) */
+ ADC1->CFGR1 |= ADC_CFGR1_DMAEN | ADC_CFGR1_DMACFG; /* (2) */
+ DMA1_Channel1->CPAR = (uint32_t) (&(ADC1->DR)); /* (3) */
+ DMA1_Channel1->CMAR = (uint32_t)(ADC_array); /* (4) */
+ DMA1_Channel1->CNDTR = NUMBER_OF_ADC_CHANNELS * 9; /* (5) */
+ DMA1_Channel1->CCR |= DMA_CCR_MINC | DMA_CCR_MSIZE_0 | DMA_CCR_PSIZE_0 | DMA_CCR_CIRC; /* (6) */
+ DMA1_Channel1->CCR |= DMA_CCR_EN; /* (7) */
+ ADC1->CR |= ADC_CR_ADSTART; /* start the ADC conversions */
+}
+
+
+/**
+ * @brief getADCval - calculate median value for `nch` channel
+ * @param nch - number of channel
+ * @return
+ */
+uint16_t getADCval(int nch){
+ int i, addr = nch;
+ register uint16_t temp;
+#define PIX_SORT(a,b) { if ((a)>(b)) PIX_SWAP((a),(b)); }
+#define PIX_SWAP(a,b) { temp=(a);(a)=(b);(b)=temp; }
+ uint16_t p[9];
+ for(i = 0; i < 9; ++i, addr += NUMBER_OF_ADC_CHANNELS) // first we should prepare array for optmed
+ p[i] = ADC_array[addr];
+ PIX_SORT(p[1], p[2]) ; PIX_SORT(p[4], p[5]) ; PIX_SORT(p[7], p[8]) ;
+ PIX_SORT(p[0], p[1]) ; PIX_SORT(p[3], p[4]) ; PIX_SORT(p[6], p[7]) ;
+ PIX_SORT(p[1], p[2]) ; PIX_SORT(p[4], p[5]) ; PIX_SORT(p[7], p[8]) ;
+ PIX_SORT(p[0], p[3]) ; PIX_SORT(p[5], p[8]) ; PIX_SORT(p[4], p[7]) ;
+ PIX_SORT(p[3], p[6]) ; PIX_SORT(p[1], p[4]) ; PIX_SORT(p[2], p[5]) ;
+ PIX_SORT(p[4], p[7]) ; PIX_SORT(p[4], p[2]) ; PIX_SORT(p[6], p[4]) ;
+ PIX_SORT(p[4], p[2]) ;
+ return p[4];
+#undef PIX_SORT
+#undef PIX_SWAP
+}
+
+// return MCU temperature (degrees of celsius * 10)
+int32_t getMCUtemp(){
+ getVdd();
+ // make correction on Vdd value
+// int32_t temperature = (int32_t)ADC_array[4] * VddValue / 330;
+ int32_t ADval = getADCval(TSENS_CHAN);
+ int32_t temperature = (int32_t) *TEMP30_CAL_ADDR - ADval;
+ temperature *= (int32_t)(1100 - 300);
+ temperature /= (int32_t)(*TEMP30_CAL_ADDR - *TEMP110_CAL_ADDR);
+ temperature += 300;
+ return(temperature);
+}
+
+// return Vdd * 100 (V)
+uint32_t getVdd(){
+ uint32_t vdd = ((uint32_t) *VREFINT_CAL_ADDR) * (uint32_t)330; // 3.3V
+ vdd /= getADCval(VREF_CHAN);
+ return vdd;
+}
+
+static inline uint32_t Ufromadu(uint8_t nch, uint32_t vdd){
+ uint32_t ADU = getADCval(nch);
+ ADU *= vdd;
+ ADU >>= 12; // /4096
+ return ADU;
+}
+
+/**
+ * @brief getUval - calculate U & I
+ * @return array with members:
+ * 0 - V12 * 100V (U12 = 12Vin/4.93)
+ * 1 - V5 * 100V (U5 = 5Vin /2)
+ * 2 - I12 mA (U = 1V/1A)
+ * 3 - V3.3* 100V (U3.3= 3.3Vin/2)
+ */
+uint16_t *getUval(){
+ static uint16_t Uval[4];
+ uint32_t vdd = getVdd();
+ uint32_t val = Ufromadu(0, vdd) * 493;
+ Uval[0] = (uint16_t)(val / 100);
+ Uval[1] = (uint16_t)(Ufromadu(1, vdd) << 1);
+ val = getADCval(2) * vdd * 10;
+ Uval[2] = (uint16_t)(val >> 12);
+ Uval[3] = (uint16_t)(Ufromadu(3, vdd) << 1);
+ return Uval;
+}
diff --git a/F0:F030,F042,F072/TSYS_controller/adc.h b/F0:F030,F042,F072/TSYS_controller/adc.h
new file mode 100644
index 0000000..62ce781
--- /dev/null
+++ b/F0:F030,F042,F072/TSYS_controller/adc.h
@@ -0,0 +1,31 @@
+/*
+ * This file is part of the TSYS_controller project.
+ * Copyright 2019 Edward V. Emelianov .
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef ADC_H
+#define ADC_H
+#include "stm32f0.h"
+
+#define NUMBER_OF_ADC_CHANNELS (6)
+
+int32_t getMCUtemp();
+uint32_t getVdd();
+uint16_t getADCval(int nch);
+void adc_setup();
+uint16_t *getUval();
+
+#endif // ADC_H
diff --git a/F0:F030,F042,F072/TSYS_controller/can.c b/F0:F030,F042,F072/TSYS_controller/can.c
new file mode 100644
index 0000000..2f9bddc
--- /dev/null
+++ b/F0:F030,F042,F072/TSYS_controller/can.c
@@ -0,0 +1,296 @@
+/*
+ * geany_encoding=koi8-r
+ * can.c
+ *
+ * Copyright 2018 Edward V. Emelianov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ */
+
+#include // memcpy
+
+#include "can.h"
+#include "hardware.h"
+#include "proto.h"
+
+
+// incoming message buffer size
+#define CAN_INMESSAGE_SIZE (32)
+
+extern volatile uint32_t Tms;
+
+// circular buffer for received messages
+static CAN_message messages[CAN_INMESSAGE_SIZE];
+static uint8_t first_free_idx = 0; // index of first empty cell
+static int8_t first_nonfree_idx = -1; // index of first data cell
+int8_t cansniffer = 0; // ==1 to listen all CAN ID's
+
+uint16_t curcanspeed = CAN_SPEED_DEFAULT; // speed of last init
+
+uint16_t CANID = 0xFFFF;
+uint8_t Controller_address = 0;
+static CAN_status can_status = CAN_STOP;
+
+static void can_process_fifo(uint8_t fifo_num);
+
+CAN_status CAN_get_status(){
+ CAN_status st = can_status;
+ // give overrun message only once
+ if(st == CAN_FIFO_OVERRUN) can_status = CAN_READY;
+ return st;
+}
+
+// push next message into buffer; return 1 if buffer overfull
+static int CAN_messagebuf_push(CAN_message *msg){
+ MSG("Try to push\n");
+ if(first_free_idx == first_nonfree_idx) return 1; // no free space
+ if(first_nonfree_idx < 0) first_nonfree_idx = 0; // first message in empty buffer
+ memcpy(&messages[first_free_idx++], msg, sizeof(CAN_message));
+ // need to roll?
+ if(first_free_idx == CAN_INMESSAGE_SIZE) first_free_idx = 0;
+ return 0;
+}
+
+// pop message from buffer
+CAN_message *CAN_messagebuf_pop(){
+ if(first_nonfree_idx < 0) return NULL;
+ CAN_message *msg = &messages[first_nonfree_idx++];
+ if(first_nonfree_idx == CAN_INMESSAGE_SIZE) first_nonfree_idx = 0;
+ if(first_nonfree_idx == first_free_idx){ // buffer is empty - refresh it
+ first_nonfree_idx = -1;
+ first_free_idx = 0;
+ }
+ return msg;
+}
+
+// get CAN address data from GPIO pins
+void readCANID(){
+ uint8_t CAN_addr = READ_CAN_INV_ADDR();
+ Controller_address = ~CAN_addr & 0x0f;
+ CANID = (CAN_ID_PREFIX & CAN_ID_MASK) | Controller_address;
+}
+
+void CAN_setup(uint16_t speed){
+ if(speed == 0) speed = curcanspeed;
+ else if(speed < CAN_SPEED_MIN) speed = CAN_SPEED_MIN;
+ else if(speed > CAN_SPEED_MAX) speed = CAN_SPEED_MAX;
+ curcanspeed = speed;
+ readCANID();
+ CAN->TSR |= CAN_TSR_ABRQ0 | CAN_TSR_ABRQ1 | CAN_TSR_ABRQ2;
+ RCC->APB1RSTR |= RCC_APB1RSTR_CANRST;
+ RCC->APB1RSTR &= ~RCC_APB1RSTR_CANRST;
+ // Configure GPIO: PB8 - CAN_Rx, PB9 - CAN_Tx
+ /* (1) Select AF mode (10) on PB8 and PB9 */
+ /* (2) AF4 for CAN signals */
+ GPIOB->MODER = (GPIOB->MODER & ~(GPIO_MODER_MODER8 | GPIO_MODER_MODER9))
+ | (GPIO_MODER_MODER8_AF | GPIO_MODER_MODER9_AF); /* (1) */
+ GPIOB->AFR[1] = (GPIOB->AFR[1] &~ (GPIO_AFRH_AFRH0 | GPIO_AFRH_AFRH1))\
+ | (4 << (0 * 4)) | (4 << (1 * 4)); /* (2) */
+ /* Enable the peripheral clock CAN */
+ RCC->APB1ENR |= RCC_APB1ENR_CANEN;
+ /* Configure CAN */
+ /* (1) Enter CAN init mode to write the configuration */
+ /* (2) Wait the init mode entering */
+ /* (3) Exit sleep mode */
+ /* (4) Normal mode, set timing to 100kb/s: BS1 = 4, BS2 = 3, prescaler = 6000/speed */
+ /* (5) Leave init mode */
+ /* (6) Wait the init mode leaving */
+ /* (7) Enter filter init mode, (16-bit + mask, filter 0 for FIFO 0) */
+ /* (8) Acivate filter 0 (1,2) */
+ /* (9) Identifier mode for bank#0, mask mode for #1 and #2 */
+ /* (10) Set the Id list */
+ /* (11) Set the mask list */
+ /* (12) Leave filter init */
+ /* (13) Set error interrupts enable */
+ CAN->MCR |= CAN_MCR_INRQ; /* (1) */
+ uint32_t tmout = 16000000;
+ while((CAN->MSR & CAN_MSR_INAK)!=CAN_MSR_INAK){ /* (2) */
+ if(--tmout == 0) break;
+ }
+ CAN->MCR &=~ CAN_MCR_SLEEP; /* (3) */
+ CAN->MCR |= CAN_MCR_ABOM;
+
+ CAN->BTR |= 2 << 20 | 3 << 16 | (6000/speed - 1); /* (4) */
+ CAN->MCR &=~ CAN_MCR_INRQ; /* (5) */
+ tmout = 16000000;
+ while((CAN->MSR & CAN_MSR_INAK)==CAN_MSR_INAK){ /* (6) */
+ if(--tmout == 0) break;
+ }
+ CAN->FMR = CAN_FMR_FINIT; /* (7) */
+ CAN->FA1R = CAN_FA1R_FACT0; /* (8) */
+ CAN->FM1R = CAN_FM1R_FBM0; /* (9) */
+ CAN->sFilterRegister[0].FR1 = CANID << 5 | ((BCAST_ID << 5) << 16); /* (10) */
+ if(cansniffer){ /* (11) */
+ CAN->FA1R |= CAN_FA1R_FACT1 | CAN_FA1R_FACT2; // activate 1 & 2
+ CAN->sFilterRegister[1].FR1 = (1<<21)|(1<<5); // all odd IDs
+ CAN->sFilterRegister[2].FR1 = (1<<21); // all even IDs
+ CAN->FFA1R = 2; // filter 1 for FIFO1, filters 0&2 - for FIFO0
+ }
+ CAN->FMR &=~ CAN_FMR_FINIT; /* (12) */
+ CAN->IER |= CAN_IER_ERRIE | CAN_IER_FOVIE0 | CAN_IER_FOVIE1; /* (13) */
+
+ /* Configure IT */
+ /* (14) Set priority for CAN_IRQn */
+ /* (15) Enable CAN_IRQn */
+ NVIC_SetPriority(CEC_CAN_IRQn, 0); /* (14) */
+ NVIC_EnableIRQ(CEC_CAN_IRQn); /* (15) */
+ can_status = CAN_READY;
+}
+
+// add filters for ALL ID's
+void CAN_listenall(){
+ cansniffer = 1;
+ CAN_setup(0);
+}
+// listen only packets to self & broadcast - delete filters 1&2
+void CAN_listenone(){
+ cansniffer = 0;
+ CAN_setup(0);
+}
+
+void can_proc(){
+ // check for messages in FIFO0 & FIFO1
+ if(CAN->RF0R & CAN_RF0R_FMP0){
+ can_process_fifo(0);
+ }
+ if(CAN->RF1R & CAN_RF1R_FMP1){
+ can_process_fifo(1);
+ }
+ if(CAN->ESR & (CAN_ESR_BOFF | CAN_ESR_EPVF | CAN_ESR_EWGF)){ // much errors - restart CAN BUS
+ mesg("too much CAN errors, restart CAN");
+ MSG("bus-off, restarting\n");
+ // request abort for all mailboxes
+ CAN->TSR |= CAN_TSR_ABRQ0 | CAN_TSR_ABRQ1 | CAN_TSR_ABRQ2;
+ // reset CAN bus
+ RCC->APB1RSTR |= RCC_APB1RSTR_CANRST;
+ RCC->APB1RSTR &= ~RCC_APB1RSTR_CANRST;
+ can_status = CAN_ERROR;
+ }
+}
+
+CAN_status can_send(uint8_t *msg, uint8_t len, uint16_t target_id){
+ if(!noLED) LED_on(LED1); // turn ON LED1 at first data sent/receive
+ uint8_t mailbox = 0;
+ // check first free mailbox
+ if(CAN->TSR & (CAN_TSR_TME)){
+ mailbox = (CAN->TSR & CAN_TSR_CODE) >> 24;
+ }else{ // no free mailboxes
+ return CAN_BUSY;
+ }
+ CAN_TxMailBox_TypeDef *box = &CAN->sTxMailBox[mailbox];
+ uint32_t lb = 0, hb = 0;
+ switch(len){
+ case 8:
+ hb |= (uint32_t)msg[7] << 24;
+ __attribute__((fallthrough));
+ case 7:
+ hb |= (uint32_t)msg[6] << 16;
+ __attribute__((fallthrough));
+ case 6:
+ hb |= (uint32_t)msg[5] << 8;
+ __attribute__((fallthrough));
+ case 5:
+ hb |= (uint32_t)msg[4];
+ __attribute__((fallthrough));
+ case 4:
+ lb |= (uint32_t)msg[3] << 24;
+ __attribute__((fallthrough));
+ case 3:
+ lb |= (uint32_t)msg[2] << 16;
+ __attribute__((fallthrough));
+ case 2:
+ lb |= (uint32_t)msg[1] << 8;
+ __attribute__((fallthrough));
+ default:
+ lb |= (uint32_t)msg[0];
+ }
+ box->TDLR = lb;
+ box->TDHR = hb;
+ box->TDTR = len;
+ box->TIR = (target_id & 0x7FF) << 21 | CAN_TI0R_TXRQ;
+ return CAN_OK;
+}
+
+static void can_process_fifo(uint8_t fifo_num){
+ if(fifo_num > 1) return;
+ CAN_FIFOMailBox_TypeDef *box = &CAN->sFIFOMailBox[fifo_num];
+ volatile uint32_t *RFxR = (fifo_num) ? &CAN->RF1R : &CAN->RF0R;
+ if(!noLED) LED_on(LED1); // turn ON LED1 at first data sent/receive
+ // read all
+ while(*RFxR & CAN_RF0R_FMP0){ // amount of messages pending
+ // CAN_RDTxR: (16-31) - timestamp, (8-15) - filter match index, (0-3) - data length
+ /* TODO: check filter match index if more than one ID can receive */
+ CAN_message msg;
+ uint8_t *dat = msg.data;
+ { // set all data to 0
+ uint32_t *dptr = (uint32_t*)msg.data;
+ dptr[0] = dptr[1] = 0;
+ }
+ uint8_t len = box->RDTR & 0x0f;
+ msg.length = len;
+ msg.ID = box->RIR >> 21;
+ if(len){ // message can be without data
+ uint32_t hb = box->RDHR, lb = box->RDLR;
+ switch(len){
+ case 8:
+ dat[7] = hb>>24;
+ __attribute__((fallthrough));
+ case 7:
+ dat[6] = (hb>>16) & 0xff;
+ __attribute__((fallthrough));
+ case 6:
+ dat[5] = (hb>>8) & 0xff;
+ __attribute__((fallthrough));
+ case 5:
+ dat[4] = hb & 0xff;
+ __attribute__((fallthrough));
+ case 4:
+ dat[3] = lb>>24;
+ __attribute__((fallthrough));
+ case 3:
+ dat[2] = (lb>>16) & 0xff;
+ __attribute__((fallthrough));
+ case 2:
+ dat[1] = (lb>>8) & 0xff;
+ __attribute__((fallthrough));
+ case 1:
+ dat[0] = lb & 0xff;
+ }
+ }
+ if(CAN_messagebuf_push(&msg)) return; // error: buffer is full, try later
+ *RFxR |= CAN_RF0R_RFOM0; // release fifo for access to next message
+ }
+ if(*RFxR & CAN_RF0R_FULL0) *RFxR &= ~CAN_RF0R_FULL0;
+}
+
+void cec_can_isr(){
+ if(CAN->RF0R & CAN_RF0R_FOVR0){ // FIFO overrun
+ CAN->RF0R &= ~CAN_RF0R_FOVR0;
+ can_status = CAN_FIFO_OVERRUN;
+ }
+ if(CAN->RF1R & CAN_RF1R_FOVR1){
+ CAN->RF1R &= ~CAN_RF1R_FOVR1;
+ can_status = CAN_FIFO_OVERRUN;
+ }
+ if(CAN->MSR & CAN_MSR_ERRI){ // Error
+ CAN->MSR &= ~CAN_MSR_ERRI;
+ // request abort for problem mailbox
+ if(CAN->TSR & CAN_TSR_TERR0) CAN->TSR |= CAN_TSR_ABRQ0;
+ if(CAN->TSR & CAN_TSR_TERR1) CAN->TSR |= CAN_TSR_ABRQ1;
+ if(CAN->TSR & CAN_TSR_TERR2) CAN->TSR |= CAN_TSR_ABRQ2;
+ }
+}
diff --git a/F0:F030,F042,F072/TSYS_controller/can.h b/F0:F030,F042,F072/TSYS_controller/can.h
new file mode 100644
index 0000000..2023c73
--- /dev/null
+++ b/F0:F030,F042,F072/TSYS_controller/can.h
@@ -0,0 +1,73 @@
+/*
+ * geany_encoding=koi8-r
+ * can.h
+ *
+ * Copyright 2018 Edward V. Emelianov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ */
+#pragma once
+#ifndef __CAN_H__
+#define __CAN_H__
+
+#include "hardware.h"
+
+// identifier mask
+#define CAN_ID_MASK ((uint16_t)0x7F0)
+// prefix of identifiers
+#define CAN_ID_PREFIX ((uint16_t)0xAAA)
+// this is master - Controller_address==0
+#define MASTER_ID (CAN_ID_PREFIX & CAN_ID_MASK)
+// broadcasting to every slave
+#define BCAST_ID ((uint16_t)0x7F7)
+// send dummy message to this ID for testing CAN bus status
+#define NOONE_ID ((uint16_t)0x7FF)
+
+extern uint16_t curcanspeed;
+extern uint16_t CANID;
+extern int8_t cansniffer;
+
+typedef struct{
+ uint8_t data[8];
+ uint8_t length;
+ uint16_t ID; // ID of receiver
+} CAN_message;
+
+typedef enum{
+ CAN_NOTMASTER, // can't send command - not a master
+ CAN_STOP, // CAN stopped
+ CAN_READY, // ready to send
+ CAN_BUSY, // bus is busy
+ CAN_OK, // all OK?
+ CAN_FIFO_OVERRUN, // FIFO overrun
+ CAN_ERROR // no recipients on bus or too many errors
+} CAN_status;
+
+CAN_status CAN_get_status();
+
+void readCANID();
+
+void CAN_setup(uint16_t speed);
+void CAN_listenall();
+void CAN_listenone();
+
+void can_proc();
+CAN_status can_send(uint8_t *msg, uint8_t len, uint16_t target_id);
+
+CAN_message *CAN_messagebuf_pop();
+
+#endif // __CAN_H__
diff --git a/F0:F030,F042,F072/TSYS_controller/can_process.c b/F0:F030,F042,F072/TSYS_controller/can_process.c
new file mode 100644
index 0000000..1310141
--- /dev/null
+++ b/F0:F030,F042,F072/TSYS_controller/can_process.c
@@ -0,0 +1,341 @@
+/*
+ * geany_encoding=koi8-r
+ * can_process.c
+ *
+ * Copyright 2018 Edward V. Emelianov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ */
+#include "adc.h"
+#include "can.h"
+#include "can_process.h"
+#include "proto.h"
+#include "sensors_manage.h"
+#include "version.inc"
+
+extern volatile uint32_t Tms; // timestamp data
+// id of master - all data will be sent to it
+static uint16_t master_id = MASTER_ID;
+
+static inline void sendmcut(uint8_t *data){
+ uint8_t t[3];
+ uint16_t T = getMCUtemp();
+ t[0] = data[1]; // command itself
+ t[1] = (T >> 8) & 0xff; // H
+ t[2] = T & 0xff; // L
+ can_send_data(t,3);
+}
+
+static inline void senduival(){
+ uint8_t buf[5];
+ uint16_t *vals = getUval();
+ buf[0] = CMD_GETUIVAL0; // V12 and V5
+ buf[1] = vals[0] >> 8; // H
+ buf[2] = vals[0] & 0xff;// L
+ buf[3] = vals[1] >> 8; // -//-
+ buf[4] = vals[1] & 0xff;
+ can_send_data(buf, 5);
+ buf[0] = CMD_GETUIVAL1; // I12 and V3.3
+ buf[1] = vals[2] >> 8; // H
+ buf[2] = vals[2] & 0xff;// L
+ buf[3] = vals[3] >> 8; // -//-
+ buf[4] = vals[3] & 0xff;
+ can_send_data(buf, 5);
+}
+
+static inline void showui(char *v1, char *v2, uint8_t *data){
+ char N = '0' + data[1];
+ addtobuf(v1);
+ bufputchar(N);
+ bufputchar('=');
+ uint16_t v = data[3]<<8 | data[4];
+ printu(v);
+ newline();
+ addtobuf(v2);
+ bufputchar(N);
+ bufputchar('=');
+ v = data[5]<<8 | data[6];
+ printu(v);
+}
+
+void can_messages_proc(){
+ CAN_message *can_mesg = CAN_messagebuf_pop();
+ if(!can_mesg) return; // no data in buffer
+ uint8_t len = can_mesg->length;
+ IWDG->KR = IWDG_REFRESH;
+#ifdef EBUG
+ SEND("got message, len: "); bufputchar('0' + len);
+ SEND(", data: ");
+ uint8_t ctr;
+ for(ctr = 0; ctr < len; ++ctr){
+ printuhex(can_mesg->data[ctr]);
+ bufputchar(' ');
+ }
+ newline();
+#endif
+ uint8_t *data = can_mesg->data, b[6];
+ b[0] = data[1];
+ // show received message in sniffer mode
+ if(cansniffer){
+ printu(Tms);
+ SEND(" #");
+ printuhex(can_mesg->ID);
+ for(int ctr = 0; ctr < len; ++ctr){
+ SEND(" ");
+ printuhex(can_mesg->data[ctr]);
+ }
+ newline();
+ }
+ // don't process alien messages
+ if(can_mesg->ID != CANID && can_mesg->ID != BCAST_ID) return;
+ int16_t t;
+ uint32_t U32;
+ if(data[0] == COMMAND_MARK){ // process commands
+ if(len < 2) return;
+ // master shouldn't react to broadcast commands!
+ if(can_mesg->ID == BCAST_ID && CANID == MASTER_ID) return;
+ switch(data[1]){
+ case CMD_DUMMY0:
+ case CMD_DUMMY1:
+ SEND("DUMMY");
+ bufputchar('0' + (data[1]==CMD_DUMMY0 ? 0 : 1));
+ newline();
+ break;
+ case CMD_PING: // pong
+ can_send_data(b, 1);
+ break;
+ case CMD_SENSORS_STATE:
+ b[1] = Sstate;
+ b[2] = sens_present[0];
+ b[3] = sens_present[1];
+ b[4] = Nsens_present;
+ b[5] = Ntemp_measured;
+ can_send_data(b, 6);
+ break;
+ case CMD_START_MEASUREMENT:
+ sensors_start();
+ break;
+ case CMD_START_SCAN:
+ sensors_scan_mode = 1;
+ break;
+ case CMD_STOP_SCAN:
+ sensors_scan_mode = 0;
+ break;
+ case CMD_SENSORS_OFF:
+ sensors_off();
+ break;
+ case CMD_LOWEST_SPEED:
+ i2c_setup(VERYLOW_SPEED);
+ break;
+ case CMD_LOW_SPEED:
+ i2c_setup(LOW_SPEED);
+ break;
+ case CMD_HIGH_SPEED:
+ i2c_setup(HIGH_SPEED);
+ break;
+ case CMD_REINIT_I2C:
+ i2c_setup(CURRENT_SPEED);
+ break;
+ case CMD_CHANGE_MASTER_B:
+ master_id = BCAST_ID;
+ break;
+ case CMD_CHANGE_MASTER:
+ master_id = MASTER_ID;
+ break;
+ case CMD_GETMCUTEMP:
+ sendmcut(data);
+ break;
+ case CMD_GETUIVAL:
+ senduival();
+ break;
+ case CMD_REINIT_SENSORS:
+ sensors_init();
+ break;
+ case CMD_GETBUILDNO:
+ b[1] = 0;
+ *((uint32_t*)&b[2]) = BUILDNO;
+ can_send_data(b, 6);
+ break;
+ case CMD_SYSTIME:
+ b[1] = 0;
+ *((uint32_t*)&b[2]) = Tms;
+ can_send_data(b, 6);
+ break;
+ }
+ }else if(data[0] == DATA_MARK){ // process received data
+ char Ns = '0' + data[1];
+ if(len < 3) return;
+ switch(data[2]){
+ case CMD_PING:
+ mesg("CMD_PING");
+ SEND("PONG");
+ bufputchar(Ns);
+ break;
+ case CMD_SENSORS_STATE:
+ mesg("CMD_SENSORS_STATE");
+ SEND("SSTATE");
+ bufputchar(Ns);
+ bufputchar('=');
+ SEND(sensors_get_statename(data[3]));
+ SEND("\nNSENS");
+ bufputchar(Ns);
+ bufputchar('=');
+ printu(data[6]);
+ SEND("\nSENSPRESENT");
+ bufputchar(Ns);
+ bufputchar('=');
+ printu(data[4] | (data[5]<<8));
+ SEND("\nNTEMP");
+ bufputchar(Ns);
+ bufputchar('=');
+ printu(data[7]);
+ break;
+ case CMD_START_MEASUREMENT: // temperature
+ mesg("CMD_START_MEASUREMENT");
+ if(len != 6) return;
+ bufputchar('T');
+ bufputchar(Ns);
+ bufputchar('_');
+ printu(data[3]);
+ bufputchar('=');
+ t = data[4]<<8 | data[5];
+ if(t < 0){
+ t = -t;
+ bufputchar('-');
+ }
+ printu(t);
+ break;
+ case CMD_GETMCUTEMP:
+ mesg("CMD_GETMCUTEMP");
+ addtobuf("TMCU");
+ bufputchar(Ns);
+ bufputchar('=');
+ t = data[3]<<8 | data[4];
+ if(t < 0){
+ bufputchar('-');
+ t = -t;
+ }
+ printu(t);
+ break;
+ case CMD_GETUIVAL0: // V12 and V5
+ mesg("CMD_GETUIVAL0");
+ showui("V12_", "V5_", data);
+ break;
+ case CMD_GETUIVAL1: // I12 and V3.3
+ mesg("CMD_GETUIVAL1");
+ showui("I12_", "V33_", data);
+ break;
+ case CMD_GETBUILDNO:
+ mesg("CMD_GETBUILDNO");
+ addtobuf("BUILDNO");
+ bufputchar(Ns);
+ bufputchar('=');
+ U32 = *((uint32_t*)&data[4]);
+ printu(U32);
+ break;
+ case CMD_SYSTIME:
+ mesg("CMD_SYSTIME");
+ addtobuf("SYSTIME");
+ bufputchar(Ns);
+ bufputchar('=');
+ U32 = *((uint32_t*)&data[4]);
+ printu(U32);
+ break;
+ default:
+ SEND("UNKNOWN_DATA");
+ }
+ newline();
+ }
+}
+
+// try to send messages, wait no more than 100ms
+static CAN_status try2send(uint8_t *buf, uint8_t len, uint16_t id){
+ uint32_t Tstart = Tms;
+ while(Tms - Tstart < SEND_TIMEOUT_MS){
+ if(CAN_OK == can_send(buf, len, id)) return CAN_OK;
+ IWDG->KR = IWDG_REFRESH;
+ }
+ SEND("CAN_BUSY\n");
+ return CAN_BUSY;
+}
+
+
+/**
+ * Send command over CAN bus (works only if controller number is 0 - master mode)
+ * @param targetID - target identifier
+ * @param cmd - command to send
+ */
+CAN_status can_send_cmd(uint16_t targetID, uint8_t cmd){
+ //if(Controller_address != 0 && cmd != CMD_DUMMY0 && cmd != CMD_DUMMY1) return CAN_NOTMASTER;
+ uint8_t buf[2];
+ buf[0] = COMMAND_MARK;
+ buf[1] = cmd;
+ return try2send(buf, 2, targetID);
+}
+
+// send data over CAN bus to MASTER_ID (not more than 6 bytes)
+CAN_status can_send_data(uint8_t *data, uint8_t len){
+ if(len > 6) return CAN_OK;
+ uint8_t buf[8];
+ buf[0] = DATA_MARK;
+ buf[1] = Controller_address;
+ int i;
+ for(i = 0; i < len; ++i) buf[i+2] = *data++;
+ return try2send(buf, len+2, master_id);
+}
+
+/**
+ * send temperature data over CAN bus once per call
+ * @return next number or -1 if all data sent
+ */
+int8_t send_temperatures(int8_t N){
+ if(N < 0 || Controller_address == 0) return -1; // don't need to send Master's data over CAN bus
+ int a, p;
+ uint8_t can_data[4];
+ int8_t retn = N;
+ can_data[0] = CMD_START_MEASUREMENT;
+ a = N / 10;
+ p = N - a*10;
+ if(p == 2){ // next sensor
+ if(++a > MUL_MAX_ADDRESS) return -1;
+ p = 0;
+ }
+ do{
+ if(!(sens_present[p] & (1< MUL_MAX_ADDRESS) return -1; // done
+ retn = a*10 + p; // current temperature sensor number
+ can_data[1] = a*10 + p;
+ //char b[] = {'T', a+'0', p+'0', '=', '+'};
+ int16_t t = Temperatures[a][p];
+ if(t == BAD_TEMPERATURE || t == NO_SENSOR){ // don't send data if it's absent on current measurement
+ ++retn;
+ }else{
+ can_data[2] = t>>8; // H byte
+ can_data[3] = t&0xff; // L byte
+ if(CAN_OK == can_send_data(can_data, 4)){ // OK, calculate next address
+ ++retn;
+ }
+ }
+ return retn;
+}
diff --git a/F0:F030,F042,F072/TSYS_controller/can_process.h b/F0:F030,F042,F072/TSYS_controller/can_process.h
new file mode 100644
index 0000000..d78fc4b
--- /dev/null
+++ b/F0:F030,F042,F072/TSYS_controller/can_process.h
@@ -0,0 +1,61 @@
+/*
+ * geany_encoding=koi8-r
+ * can_process.h
+ *
+ * Copyright 2018 Edward V. Emelianov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ */
+#include "can.h"
+
+// timeout for trying to send data
+#define SEND_TIMEOUT_MS (10)
+// mark first byte if command sent
+#define COMMAND_MARK (0xA5)
+// mark first byte if data sent
+#define DATA_MARK (0x5A)
+
+// 8-bit commands sent by master
+typedef enum{
+ CMD_PING, // request for PONG cmd
+ CMD_START_MEASUREMENT, // start thermal measurement (and turn ON sensors if was OFF)
+ CMD_SENSORS_STATE, // reply data with sensors state (data: 0 - SState, 1,2 - sens_present0, 3 - Nsens_presend, 4 - Ntemp_measured)
+ CMD_START_SCAN, // run scan mode @ all controllers
+ CMD_STOP_SCAN, // stop scan mode
+ CMD_SENSORS_OFF, // turn off power of sensors
+ CMD_LOWEST_SPEED, // lowest I2C speed
+ CMD_LOW_SPEED, // low I2C speed (10kHz)
+ CMD_HIGH_SPEED, // high I2C speed (100kHz)
+ CMD_REINIT_I2C, // reinit I2C with current speed
+ CMD_CHANGE_MASTER_B, // change master id to broadcast
+ CMD_CHANGE_MASTER, // change master id to 0
+ CMD_GETMCUTEMP, // MCU temperature value
+ CMD_GETUIVAL, // request to get values of V12, V5, I12 and V3.3
+ CMD_GETUIVAL0, // answer with values of V12 and V5
+ CMD_GETUIVAL1, // answer with values of I12 and V3.3
+ CMD_REINIT_SENSORS, // (re)init sensors
+ CMD_GETBUILDNO, // request for firmware build number
+ CMD_SYSTIME, // get system time
+ // dummy commands for test purposes
+ CMD_DUMMY0 = 0xDA,
+ CMD_DUMMY1 = 0xAD
+} CAN_commands;
+
+void can_messages_proc();
+CAN_status can_send_cmd(uint16_t targetID, uint8_t cmd);
+CAN_status can_send_data(uint8_t *data, uint8_t len);
+int8_t send_temperatures(int8_t N);
diff --git a/F0:F030,F042,F072/TSYS_controller/hardware.c b/F0:F030,F042,F072/TSYS_controller/hardware.c
new file mode 100644
index 0000000..f1a0e8f
--- /dev/null
+++ b/F0:F030,F042,F072/TSYS_controller/hardware.c
@@ -0,0 +1,99 @@
+/*
+ * geany_encoding=koi8-r
+ * hardware.c - hardware-dependent macros & functions
+ *
+ * Copyright 2018 Edward V. Emelianov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ */
+
+#include "hardware.h"
+
+I2C_SPEED curI2Cspeed = LOW_SPEED;
+
+
+void gpio_setup(void){
+ // here we turn on clocking for all periph.
+ RCC->AHBENR |= RCC_AHBENR_GPIOBEN | RCC_AHBENR_GPIOAEN | RCC_AHBENR_DMAEN;
+ // Set LEDS (PB10/11) & multiplexer as output (PB0..2,12)
+ GPIOB->MODER = (GPIOB->MODER & ~(GPIO_MODER_MODER10 | GPIO_MODER_MODER11 |
+ GPIO_MODER_MODER0 | GPIO_MODER_MODER1 |
+ GPIO_MODER_MODER2 | GPIO_MODER_MODER12 )
+ ) |
+ GPIO_MODER_MODER10_O | GPIO_MODER_MODER11_O |
+ GPIO_MODER_MODER0_O | GPIO_MODER_MODER1_O |
+ GPIO_MODER_MODER2_O | GPIO_MODER_MODER12_O;
+ // multiplexer outputs are push-pull, GPIOB->OTYPER = 0
+ MUL_OFF();
+ // PA8 - power enable
+ GPIOA->MODER = (GPIOA->MODER & ~(GPIO_MODER_MODER8)) |
+ GPIO_MODER_MODER8_O;
+ // PA13..15 (low bits) + PB15 (high bit) - CAN address, pullup inputs
+ GPIOA->PUPDR = (GPIOA->PUPDR & ~(GPIO_PUPDR_PUPDR13 | GPIO_PUPDR_PUPDR14 |
+ GPIO_PUPDR_PUPDR15)
+ ) |
+ GPIO_PUPDR_PUPDR13_0 | GPIO_PUPDR_PUPDR14_0 |
+ GPIO_PUPDR_PUPDR15_0;
+ GPIOB->PUPDR = (GPIOB->PUPDR & ~(GPIO_PUPDR_PUPDR15)) |
+ GPIO_PUPDR_PUPDR15_0;
+ pin_set(LED0_port, LED0_pin); // clear LEDs
+ pin_set(LED1_port, LED1_pin);
+}
+
+void i2c_setup(I2C_SPEED speed){
+ if(speed == CURRENT_SPEED){
+ speed = curI2Cspeed;
+ }else{
+ curI2Cspeed = speed;
+ }
+ I2C1->CR1 = 0;
+#if I2CPINS == 910
+/*
+ * GPIO Resources: I2C1_SCL - PA9, I2C1_SDA - PA10
+ * GPIOA->AFR[1]
+ */
+ GPIOA->AFR[1] &= ~0xff0; // alternate function F4 for PA9/PA10
+ GPIOA->AFR[1] |= 0x440;
+ GPIOA->MODER &= ~(GPIO_MODER_MODER9 | GPIO_MODER_MODER10);
+ GPIOA->MODER |= GPIO_MODER_MODER9_AF | GPIO_MODER_MODER10_AF; // alternate function
+ GPIOA->OTYPER |= GPIO_OTYPER_OT_9 | GPIO_OTYPER_OT_10; // opendrain
+ //GPIOA->OTYPER |= GPIO_OTYPER_OT_10; // opendrain
+#elif I2CPINS == 67
+/*
+ * GPIO Resources: I2C1_SCL - PB6, I2C1_SDA - PB7 (AF1)
+ * GPIOB->AFR[0] -> 1<<6*4 | 1<<7*4 = 0x11000000
+ */
+ GPIOB->AFR[0] = (GPIOB->AFR[0] & ~0xff000000) | 0x11000000;
+ GPIOB->MODER = (GPIOB->MODER & ~(GPIO_MODER_MODER6 | GPIO_MODER_MODER7)) |
+ GPIO_MODER_MODER6_AF | GPIO_MODER_MODER7_AF;
+ GPIOB->OTYPER |= GPIO_OTYPER_OT_6 | GPIO_OTYPER_OT_7;
+#else // undefined
+#error "Not implemented"
+#endif
+ // I2C
+ RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; // timing
+ RCC->CFGR3 |= RCC_CFGR3_I2C1SW; // use sysclock for timing
+ if(speed == LOW_SPEED){ // 10kHz
+ // PRESC=B, SCLDEL=4, SDADEL=2, SCLH=0xC3, SCLL=0xB0
+ I2C1->TIMINGR = (0xB<<28) | (4<<20) | (2<<16) | (0xC3<<8) | (0xB0);
+ }else if(speed == HIGH_SPEED){ // 100kHz
+ I2C1->TIMINGR = (0xB<<28) | (4<<20) | (2<<16) | (0x12<<8) | (0x11);
+ }else{ // VERYLOW_SPEED - the lowest speed by STM register: 5.8kHz (presc = 16-1 = 15; )
+ I2C1->TIMINGR = (0xf<<28) | (4<<20) | (2<<16) | (0xff<<8) | (0xff);
+ }
+ I2C1->CR1 = I2C_CR1_PE;// | I2C_CR1_RXIE; // Enable I2C & (interrupt on receive - not supported yet)
+}
diff --git a/F0:F030,F042,F072/TSYS_controller/hardware.h b/F0:F030,F042,F072/TSYS_controller/hardware.h
new file mode 100644
index 0000000..c84ac00
--- /dev/null
+++ b/F0:F030,F042,F072/TSYS_controller/hardware.h
@@ -0,0 +1,97 @@
+/*
+ * geany_encoding=koi8-r
+ * hardware.h
+ *
+ * Copyright 2018 Edward V. Emelianov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ */
+#pragma once
+#ifndef __HARDWARE_H__
+#define __HARDWARE_H__
+
+#include "stm32f0.h"
+
+#define CAN_SPEED_DEFAULT (250)
+#define CAN_SPEED_MIN (12)
+#define CAN_SPEED_MAX (1000)
+
+// LED0
+#define LED0_port GPIOB
+#define LED0_pin (1<<10)
+// LED1
+#define LED1_port GPIOB
+#define LED1_pin (1<<11)
+
+#ifndef USARTNUM
+#define USARTNUM 2
+#endif
+
+#define CONCAT(a,b) a ## b
+#define STR_HELPER(s) #s
+#define STR(s) STR_HELPER(s)
+
+#define FORMUSART(X) CONCAT(USART, X)
+#define USARTX FORMUSART(USARTNUM)
+
+#ifndef I2CPINS
+#define I2CPINS 910
+#endif
+
+#ifndef LED1_port
+#define LED1_port LED0_port
+#endif
+#ifndef LED1_pin
+#define LED1_pin LED0_pin
+#endif
+#define LED_blink(x) pin_toggle(x ## _port, x ## _pin)
+#define LED_on(x) pin_clear(x ## _port, x ## _pin)
+#define LED_off(x) pin_set(x ## _port, x ## _pin)
+
+// set active channel number
+#define MUL_ADDRESS(x) do{GPIOB->BSRR = (0x7 << 16) | (x);}while(0)
+// address from 0 to 7
+// WARNING!!! In current case all variables for sensors counting are uint8_t, so if
+// MUL_MAX_ADDRESS would be greater than 7 you need to edit all codes!!!11111111111111111111
+#define MUL_MAX_ADDRESS (7)
+// turn multiplexer on/off (PB12 -> 1/0)
+#define MUL_ON() pin_clear(GPIOB, (1<<12))
+#define MUL_OFF() pin_set(GPIOB, (1<<12))
+
+// turn on/off power of sensors (PA8-> 1/0)
+#define SENSORS_ON() pin_set(GPIOA, (1<<8))
+#define SENSORS_OFF() pin_clear(GPIOA, (1<<8))
+// check overcurrent (PB3 == 0)
+#define SENSORS_OVERCURNT() ((1<<3) != (GPIOB->IDR & (1<<3)))
+
+// CAN address - PA13..PA15
+#define READ_CAN_INV_ADDR() (((GPIOA->IDR & (0x7<<13))>>13) | ((GPIOB->IDR & (1<<15)) >> 12))
+extern uint8_t Controller_address;
+
+typedef enum{
+ VERYLOW_SPEED,
+ LOW_SPEED,
+ HIGH_SPEED,
+ CURRENT_SPEED
+} I2C_SPEED;
+
+extern I2C_SPEED curI2Cspeed;
+
+void gpio_setup(void);
+void i2c_setup(I2C_SPEED speed);
+
+#endif // __HARDWARE_H__
diff --git a/F0:F030,F042,F072/TSYS_controller/i2c.c b/F0:F030,F042,F072/TSYS_controller/i2c.c
new file mode 100644
index 0000000..16ced93
--- /dev/null
+++ b/F0:F030,F042,F072/TSYS_controller/i2c.c
@@ -0,0 +1,129 @@
+/*
+ * geany_encoding=koi8-r
+ * i2c.c
+ *
+ * Copyright 2017 Edward V. Emelianov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ */
+#include "hardware.h"
+#include "i2c.h"
+
+/**
+ * I2C for TSYS01
+ * Speed <= 400kHz (200)
+ * t_SCLH > 21ns
+ * t_SCLL > 21ns
+ * while reading, sends NACK
+ * after reading get 24bits of T value, we need upper 2 bytes: ADC16 = ADC>>8
+ * T = (-2) * k4 * 10^{-21} * ADC16^4
+ * + 4 * k3 * 10^{-16} * ADC16^3
+ * + (-2) * k2 * 10^{-11} * ADC16^2
+ * + 1 * k1 * 10^{-6} * ADC16
+ * +(-1.5)* k0 * 10^{-2}
+ * All coefficiens are in registers:
+ * k4 - 0xA2, k3 - 0xA4, k2 - 0xA6, k1 - 0xA8, k0 - 0xAA
+ */
+
+extern volatile uint32_t Tms;
+static uint32_t cntr;
+
+/**
+ * write command byte to I2C
+ * @param addr - device address (TSYS01_ADDR0 or TSYS01_ADDR1)
+ * @param data - byte to write
+ * @return 0 if error
+ */
+uint8_t write_i2c(uint8_t addr, uint8_t data){
+ cntr = Tms;
+ I2C1->ICR = 0x3f38; // clear all errors
+ while(I2C1->ISR & I2C_ISR_BUSY){
+ IWDG->KR = IWDG_REFRESH;
+ if(Tms - cntr > I2C_TIMEOUT){
+ return 0; // check busy
+ }}
+ cntr = Tms;
+ while(I2C1->CR2 & I2C_CR2_START){
+ IWDG->KR = IWDG_REFRESH;
+ if(Tms - cntr > I2C_TIMEOUT){
+ return 0; // check start
+ }}
+ //I2C1->ICR = 0x3f38; // clear all errors
+ I2C1->CR2 = 1<<16 | addr | I2C_CR2_AUTOEND; // 1 byte, autoend
+ // now start transfer
+ I2C1->CR2 |= I2C_CR2_START;
+ cntr = Tms;
+ while(!(I2C1->ISR & I2C_ISR_TXIS)){ // ready to transmit
+ IWDG->KR = IWDG_REFRESH;
+ if(I2C1->ISR & I2C_ISR_NACKF){
+ I2C1->ICR |= I2C_ICR_NACKCF;
+ return 0;
+ }
+ if(Tms - cntr > I2C_TIMEOUT){
+ return 0;
+ }
+ }
+ I2C1->TXDR = data; // send data
+ // wait for data gone
+ while(I2C1->ISR & I2C_ISR_BUSY){
+ IWDG->KR = IWDG_REFRESH;
+ if(Tms - cntr > I2C_TIMEOUT){break;}
+ }
+ return 1;
+}
+
+/**
+ * read nbytes (2 or 3) of data from I2C line
+ * @return 1 if all OK, 0 if NACK or no device found
+ */
+uint8_t read_i2c(uint8_t addr, uint32_t *data, uint8_t nbytes){
+ uint32_t result = 0;
+ cntr = Tms;
+ //MSG("read_i2c\n");
+ while(I2C1->ISR & I2C_ISR_BUSY){
+ IWDG->KR = IWDG_REFRESH;
+ if(Tms - cntr > I2C_TIMEOUT){
+ return 0; // check busy
+ }}
+ cntr = Tms;
+ while(I2C1->CR2 & I2C_CR2_START){
+ IWDG->KR = IWDG_REFRESH;
+ if(Tms - cntr > I2C_TIMEOUT){
+ return 0; // check start
+ }}
+ // I2C1->ICR = 0x3f38; // clear all errors
+ // read N bytes
+ I2C1->CR2 = (nbytes<<16) | addr | 1 | I2C_CR2_AUTOEND | I2C_CR2_RD_WRN;
+ I2C1->CR2 |= I2C_CR2_START;
+ uint8_t i;
+ cntr = Tms;
+ for(i = 0; i < nbytes; ++i){
+ while(!(I2C1->ISR & I2C_ISR_RXNE)){ // wait for data
+ IWDG->KR = IWDG_REFRESH;
+ if(I2C1->ISR & I2C_ISR_NACKF){
+ I2C1->ICR |= I2C_ICR_NACKCF;
+ return 0;
+ }
+ if(Tms - cntr > I2C_TIMEOUT){
+ return 0;
+ }
+ }
+ result = (result << 8) | I2C1->RXDR;
+ }
+ *data = result;
+ return 1;
+ }
diff --git a/F0:F030,F042,F072/TSYS_controller/i2c.h b/F0:F030,F042,F072/TSYS_controller/i2c.h
new file mode 100644
index 0000000..7c6ab65
--- /dev/null
+++ b/F0:F030,F042,F072/TSYS_controller/i2c.h
@@ -0,0 +1,41 @@
+/*
+ * geany_encoding=koi8-r
+ * i2c.h
+ *
+ * Copyright 2017 Edward V. Emelianov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ */
+
+#include "stm32f0.h"
+
+// timeout of I2C bus in ms
+#define I2C_TIMEOUT (100)
+// CSB=1, address 1110110
+#define TSYS01_ADDR0 (0x76 << 1)
+// CSB=0, address 1110111
+#define TSYS01_ADDR1 (0x77 << 1)
+// registers: reset, read ADC value, start converstion, start of PROM
+#define TSYS01_RESET (0x1E)
+#define TSYS01_ADC_READ (0x00)
+#define TSYS01_START_CONV (0x48)
+#define TSYS01_PROM_ADDR0 (0xA0)
+// conversion time (with reserve)
+#define CONV_TIME (15)
+
+uint8_t read_i2c(uint8_t addr, uint32_t *data, uint8_t nbytes);
+uint8_t write_i2c(uint8_t addr, uint8_t data);
diff --git a/F0:F030,F042,F072/TSYS_controller/ld/stm32f042k.ld b/F0:F030,F042,F072/TSYS_controller/ld/stm32f042k.ld
new file mode 100644
index 0000000..e747253
--- /dev/null
+++ b/F0:F030,F042,F072/TSYS_controller/ld/stm32f042k.ld
@@ -0,0 +1,12 @@
+/* Linker script for STM32F042x6, 32K flash, 6K RAM. */
+
+/* Define memory regions. */
+MEMORY
+{
+ rom (rx) : ORIGIN = 0x08000000, LENGTH = 32K
+ ram (rwx) : ORIGIN = 0x20000000, LENGTH = 6K
+}
+
+/* Include the common ld script. */
+INCLUDE stm32f0.ld
+
diff --git a/F0:F030,F042,F072/TSYS_controller/main.c b/F0:F030,F042,F072/TSYS_controller/main.c
new file mode 100644
index 0000000..64a12ff
--- /dev/null
+++ b/F0:F030,F042,F072/TSYS_controller/main.c
@@ -0,0 +1,135 @@
+/*
+ * main.c
+ *
+ * Copyright 2017 Edward V. Emelianoff
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+#include "adc.h"
+#include "can.h"
+#include "can_process.h"
+#include "hardware.h"
+#include "i2c.h"
+#include "proto.h"
+#include "sensors_manage.h"
+#include "usart.h"
+#include "usb.h"
+
+#pragma message("USARTNUM=" STR(USARTNUM))
+#pragma message("I2CPINS=" STR(I2CPINS))
+#ifdef EBUG
+#pragma message("Debug mode")
+#else
+#pragma message("Release mode")
+#endif
+
+volatile uint32_t Tms = 0;
+volatile uint8_t canerror = 0;
+
+/* Called when systick fires */
+void sys_tick_handler(void){
+ ++Tms;
+}
+
+static void iwdg_setup(){
+ /* Enable the peripheral clock RTC */
+ /* (1) Enable the LSI (40kHz) */
+ /* (2) Wait while it is not ready */
+ RCC->CSR |= RCC_CSR_LSION; /* (1) */
+ while((RCC->CSR & RCC_CSR_LSIRDY) != RCC_CSR_LSIRDY); /* (2) */
+ /* Configure IWDG */
+ /* (1) Activate IWDG (not needed if done in option bytes) */
+ /* (2) Enable write access to IWDG registers */
+ /* (3) Set prescaler by 64 (1.6ms for each tick) */
+ /* (4) Set reload value to have a rollover each 2s */
+ /* (5) Check if flags are reset */
+ /* (6) Refresh counter */
+ IWDG->KR = IWDG_START; /* (1) */
+ IWDG->KR = IWDG_WRITE_ACCESS; /* (2) */
+ IWDG->PR = IWDG_PR_PR_1; /* (3) */
+ IWDG->RLR = 1250; /* (4) */
+ while(IWDG->SR); /* (5) */
+ IWDG->KR = IWDG_REFRESH; /* (6) */
+}
+
+int main(void){
+ uint32_t lastT = 0, lastS = 0, lastB = 0;
+ uint8_t gotmeasurement = 0;
+ char inbuf[256];
+ sysreset();
+ SysTick_Config(6000, 1);
+ gpio_setup();
+ adc_setup();
+ usart_setup();
+ i2c_setup(LOW_SPEED);
+ readCANID();
+ if(CANID == MASTER_ID) cansniffer = 1; // MASTER in sniffer mode by default
+ CAN_setup(0); // setup with default 250kbaud
+ RCC->CSR |= RCC_CSR_RMVF; // remove reset flags
+ USB_setup();
+ sensors_init();
+ iwdg_setup();
+
+ while (1){
+ IWDG->KR = IWDG_REFRESH; // refresh watchdog
+ if(lastT > Tms || Tms - lastT > 499){
+ if(!noLED) LED_blink(LED0);
+ lastT = Tms;
+ // send dummy command to noone to test CAN bus
+ //can_send_cmd(NOONE_ID, CMD_DUMMY0);
+ }
+ if(lastS != Tms){ // run sensors proc. once per 1ms
+ sensors_process();
+ lastS = Tms;
+ if(SENS_SLEEPING == Sstate){ // show temperature @ each sleeping occurence
+ if(!gotmeasurement){
+ gotmeasurement = 1;
+ showtemperature();
+ }
+ }else{
+ if(SENS_WAITING == Sstate) gotmeasurement = 0;
+ }
+ }
+ usb_proc();
+ can_proc();
+ CAN_status stat = CAN_get_status();
+ if(stat == CAN_FIFO_OVERRUN){
+ SEND("CAN bus fifo overrun occured!\n");
+ }else if(stat == CAN_ERROR){
+ if(!noLED) LED_off(LED1);
+ CAN_setup(0);
+ canerror = 1;
+ }
+ can_messages_proc();
+ IWDG->KR = IWDG_REFRESH;
+ uint8_t r = 0;
+ if((r = USB_receive(inbuf, 255))){
+ inbuf[r] = 0;
+ cmd_parser(inbuf, 1);
+ }
+ if(usartrx()){ // usart1 received data, store it in buffer
+ char *txt = NULL;
+ r = usart_getline(&txt);
+ txt[r] = 0;
+ cmd_parser(txt, 0);
+ }
+ if(lastB - Tms > 99){ // run `sendbuf` each 100ms
+ sendbuf();
+ lastB = Tms;
+ }
+ }
+ return 0;
+}
diff --git a/F0:F030,F042,F072/TSYS_controller/proto.c b/F0:F030,F042,F072/TSYS_controller/proto.c
new file mode 100644
index 0000000..4486cc4
--- /dev/null
+++ b/F0:F030,F042,F072/TSYS_controller/proto.c
@@ -0,0 +1,527 @@
+/*
+ * geany_encoding=koi8-r
+ * proto.c
+ *
+ * Copyright 2018 Edward V. Emelianov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ */
+
+#include
+
+#include "adc.h"
+#include "can.h"
+#include "can_process.h"
+#include "hardware.h"
+#include "proto.h"
+#include "sensors_manage.h"
+#include "usart.h"
+#include "usb.h"
+#include "version.inc"
+
+extern volatile uint8_t canerror;
+extern volatile uint32_t Tms;
+
+static char buff[UARTBUFSZ+1], /* +1 - for USB send (it receive \0-terminated line) */ *bptr = buff;
+static int blen = 0, USBcmd = 0, debugmode = 0;
+// LEDs are OFF by default
+uint8_t noLED =
+#ifdef EBUG
+ 0
+#else
+ 1
+#endif
+;
+
+void sendbuf(){
+ IWDG->KR = IWDG_REFRESH;
+ if(blen == 0) return;
+ *bptr = 0;
+ if(USBcmd) USB_send(buff);
+ else while(LINE_BUSY == usart_send(buff, blen)){IWDG->KR = IWDG_REFRESH;}
+ bptr = buff;
+ blen = 0;
+}
+
+void addtobuf(const char *txt){
+ IWDG->KR = IWDG_REFRESH;
+ int l = strlen(txt);
+ if(l > UARTBUFSZ){
+ sendbuf(); // send prevoius data in buffer
+ if(USBcmd) USB_send(txt);
+ else while(LINE_BUSY == usart_send_blocking(txt, l)){IWDG->KR = IWDG_REFRESH;}
+ }else{
+ if(blen+l > UARTBUFSZ){
+ sendbuf();
+ }
+ strcpy(bptr, txt);
+ bptr += l;
+ }
+ *bptr = 0;
+ blen += l;
+}
+
+void bufputchar(char ch){
+ if(blen > UARTBUFSZ-1){
+ sendbuf();
+ }
+ *bptr++ = ch;
+ ++blen;
+}
+
+static void CANsend(uint16_t targetID, uint8_t cmd, char echo){
+ if(CAN_OK == can_send_cmd(targetID, cmd)){
+ bufputchar(echo);
+ bufputchar('\n');
+ }
+}
+
+// show all ADC values
+static inline void showADCvals(){
+ char msg[] = "ADCn=";
+ for(int n = 0; n < NUMBER_OF_ADC_CHANNELS; ++n){
+ msg[3] = n + '0';
+ addtobuf(msg);
+ printu(getADCval(n));
+ newline();
+ }
+}
+
+static inline void printmcut(){
+ SEND("MCUT=");
+ int32_t T = getMCUtemp();
+ if(T < 0){
+ bufputchar('-');
+ T = -T;
+ }
+ printu(T);
+ newline();
+}
+
+static inline void showUIvals(){
+ uint16_t *vals = getUval();
+ SEND("V12="); printu(vals[0]);
+ SEND("\nV5="); printu(vals[1]);
+ SEND("\nV33="); printu(vals[3]);
+ SEND("\nI12="); printu(vals[2]);
+ newline();
+}
+
+static char *omit_spaces(char *buf){
+ while(*buf){
+ if(*buf > ' ') break;
+ ++buf;
+ }
+ return buf;
+}
+
+static inline void setCANbrate(char *str){
+ if(!str || !*str) return;
+ int32_t spd = 0;
+ str = omit_spaces(str);
+ char *e = getnum(str, &spd);
+ if(e == str){
+ SEND("BAUDRATE=");
+ printu(curcanspeed);
+ newline();
+ return;
+ }
+ if(spd < CAN_SPEED_MIN || spd > CAN_SPEED_MAX){
+ SEND("Wrong speed\n");
+ return;
+ }
+ CAN_setup(spd);
+ SEND("OK\n");
+}
+
+// parse `txt` to CAN_message
+static CAN_message *parseCANmsg(char *txt){
+ static CAN_message canmsg;
+ int32_t N;
+ char *n;
+ int ctr = -1;
+ canmsg.ID = 0xffff;
+ do{
+ txt = omit_spaces(txt);
+ n = getnum(txt, &N);
+ if(txt == n) break;
+ txt = n;
+ if(ctr == -1){
+ if(N > 0x7ff){
+ SEND("ID should be 11-bit number!\n");
+ return NULL;
+ }
+ canmsg.ID = (uint16_t)(N&0x7ff);
+ ctr = 0;
+ continue;
+ }
+ if(ctr > 7){
+ SEND("ONLY 8 data bytes allowed!\n");
+ return NULL;
+ }
+ if(N > 0xff){
+ SEND("Every data portion is a byte!\n");
+ return NULL;
+ }
+ canmsg.data[ctr++] = (uint8_t)(N&0xff);
+ }while(1);
+ if(canmsg.ID == 0xffff){
+ SEND("NO ID given, send nothing!\n");
+ return NULL;
+ }
+ SEND("Message parsed OK\n");
+ canmsg.length = (uint8_t) ctr;
+ return &canmsg;
+}
+
+// send command, format: ID (hex/bin/dec) data bytes (up to 8 bytes, space-delimeted)
+static void sendCANcommand(char *txt){
+ CAN_message *msg = parseCANmsg(txt);
+ if(!msg) return;
+ uint32_t N = 1000;
+ while(CAN_BUSY == can_send(msg->data, msg->length, msg->ID)){
+ if(--N == 0) break;
+ }
+}
+
+/**
+ * @brief cmd_parser - command parsing
+ * @param txt - buffer with commands & data
+ * @param isUSB - == 1 if data got from USB
+ */
+void cmd_parser(char *txt, uint8_t isUSB){
+ USBcmd = isUSB;
+ int16_t L = strlen(txt), ID = BCAST_ID;
+ char _1st = txt[0];
+ if(_1st >= '0' && _1st < '8'){ // send command to Nth controller, not broadcast
+ if(L == 3){ // with '\n' at end!
+ ID = (CAN_ID_PREFIX & CAN_ID_MASK) | (_1st - '0');
+ _1st = txt[1];
+ }else{
+ _1st = '?'; // show help
+ }
+ }
+ switch(_1st){
+ case '@':
+ debugmode = !debugmode;
+ SEND("DEBUG mode ");
+ if(debugmode) SEND("ON");
+ else SEND("OFF");
+ newline();
+ break;
+ case 'a':
+ showADCvals();
+ break;
+ case 'B':
+ CANsend(ID, CMD_DUMMY0, _1st);
+ break;
+ case 'b':
+ setCANbrate(txt + 1);
+ break;
+ case 'c':
+ showcoeffs();
+ break;
+ case 'D':
+ CANsend(MASTER_ID, CMD_DUMMY1, _1st);
+ break;
+ case 'd':
+ SEND("Can address: ");
+ printuhex(CANID);
+ newline();
+ break;
+ case 'E':
+ CANsend(ID, CMD_STOP_SCAN, _1st);
+ break;
+ case 'e':
+ sensors_scan_mode = 0;
+ break;
+ case 'F':
+ CANsend(ID, CMD_SENSORS_OFF, _1st);
+ break;
+ case 'f':
+ sensors_off();
+ break;
+ case 'g':
+ SEND("Group ID (sniffer) CAN mode\n");
+ CAN_listenall();
+ break;
+ case 'H':
+ CANsend(ID, CMD_HIGH_SPEED, _1st);
+ break;
+ case 'h':
+ i2c_setup(HIGH_SPEED);
+ break;
+ case 'I':
+ CANsend(ID, CMD_REINIT_SENSORS, _1st);
+ break;
+ case 'i':
+ sensors_init();
+ break;
+ case 'J':
+ CANsend(ID, CMD_GETMCUTEMP, _1st);
+ break;
+ case 'j':
+ printmcut();
+ break;
+ case 'K':
+ CANsend(ID, CMD_GETUIVAL, _1st);
+ break;
+ case 'k':
+ showUIvals();
+ break;
+ case 'L':
+ CANsend(ID, CMD_LOW_SPEED, _1st);
+ break;
+ case 'l':
+ i2c_setup(LOW_SPEED);
+ break;
+ case 'M':
+ CANsend(ID, CMD_CHANGE_MASTER_B, _1st);
+ break;
+ case 'm':
+ CANsend(ID, CMD_CHANGE_MASTER, _1st);
+ break;
+ case 'N':
+ CANsend(ID, CMD_GETBUILDNO, _1st);
+ break;
+ case 'O':
+ noLED = 0;
+ SEND("LED on\n");
+ break;
+ case 'o':
+ noLED = 1;
+ LED_off(LED0);
+ LED_off(LED1);
+ SEND("LED off\n");
+ break;
+ case 'P':
+ CANsend(ID, CMD_PING, _1st);
+ break;
+ case 'Q':
+ CANsend(ID, CMD_SYSTIME, _1st);
+ break;
+ case 'q':
+ SEND("SYSTIME0="); printu(Tms); newline();
+ break;
+ case 'R':
+ CANsend(ID, CMD_REINIT_I2C, _1st);
+ break;
+ case 'r':
+ i2c_setup(CURRENT_SPEED);
+ break;
+ case 's':
+ sendCANcommand(txt+1);
+ break;
+ case 'T':
+ CANsend(ID, CMD_START_MEASUREMENT, _1st);
+ break;
+ case 't':
+ if(!sensors_scan_mode) sensors_start();
+ break;
+ case 'u':
+ SEND("Unique ID CAN mode\n");
+ CAN_listenone();
+ break;
+ case 'V':
+ CANsend(ID, CMD_LOWEST_SPEED, _1st);
+ break;
+ case 'v':
+ i2c_setup(VERYLOW_SPEED);
+ break;
+ case 'X':
+ CANsend(ID, CMD_START_SCAN, _1st);
+ break;
+ case 'x':
+ sensors_scan_mode = 1;
+ break;
+ case 'Y':
+ CANsend(ID, CMD_SENSORS_STATE, _1st);
+ break;
+ case 'y':
+ SEND("SSTATE0=");
+ SEND(sensors_get_statename(Sstate));
+ SEND("\nNSENS0=");
+ printu(Nsens_present);
+ SEND("\nSENSPRESENT0=");
+ printu(sens_present[0] | (sens_present[1]<<8));
+ SEND("\nNTEMP0=");
+ printu(Ntemp_measured);
+ newline();
+ break;
+ case 'z':
+ SEND("CANERROR=");
+ if(canerror){
+ canerror = 0;
+ bufputchar('1');
+ }else bufputchar('0');
+ newline();
+ break;
+ default: // help
+ SEND("https://github.com/eddyem/tsys01/tree/master/STM32/TSYS_controller build#" BUILD_NUMBER " @ " BUILD_DATE "\n");
+ SEND(
+ "ALL little letters - without CAN messaging\n"
+ "0..7 - send command to given controller (0 - this) instead of broadcast\n"
+ "@ - set/reset debug mode\n"
+ "a - get raw ADC values\n"
+ "B - send broadcast CAN dummy message\n"
+ "b - get/set CAN bus baudrate\n"
+ "c - show coefficients (current)\n"
+ "d - get last CAN address\n"
+ "D - send CAN dummy message to master\n"
+ "Ee- end themperature scan\n"
+ "Ff- turn oFf sensors\n"
+ "g - group (sniffer) CAN mode\n"
+ "Hh- high I2C speed\n"
+ "Ii- (re)init sensors\n"
+ "Jj- get MCU temperature\n"
+ "Kk- get U/I values\n"
+ "Ll- low I2C speed\n"
+ "Mm- change master id to 0 (m) / broadcast (M)\n"
+ "N - get build number\n"
+ "Oo- turn onboard diagnostic LEDs *O*n or *o*ff (both commands are local)\n"
+ "P - ping everyone over CAN\n"
+ "Qq- get system time\n"
+ "Rr- reinit I2C\n"
+ "s - send CAN message\n"
+ "Tt- start temperature measurement\n"
+ "u - unique ID (default) CAN mode\n"
+ "Vv- very low I2C speed\n"
+ "Xx- Start themperature scan\n"
+ "Yy- get sensors state\n"
+ "z - check CAN status for errors\n"
+ );
+ break;
+ }
+}
+
+// print 32bit unsigned int
+void printu(uint32_t val){
+ char buf[11], *bufptr = &buf[10];
+ *bufptr = 0;
+ if(!val){
+ *(--bufptr) = '0';
+ }else{
+ while(val){
+ register uint32_t o = val;
+ val /= 10;
+ *(--bufptr) = (o - 10*val) + '0';
+ }
+ }
+ addtobuf(bufptr);
+}
+
+// print 32bit unsigned int as hex
+void printuhex(uint32_t val){
+ addtobuf("0x");
+ uint8_t *ptr = (uint8_t*)&val + 3;
+ int i, j, z = 1;
+ for(i = 0; i < 4; ++i, --ptr){
+ if(*ptr == 0){ // omit leading zeros
+ if(i == 3) z = 0;
+ if(z) continue;
+ }
+ else z = 0;
+ for(j = 1; j > -1; --j){
+ uint8_t half = (*ptr >> (4*j)) & 0x0f;
+ if(half < 10) bufputchar(half + '0');
+ else bufputchar(half - 10 + 'a');
+ }
+ }
+}
+
+// THERE'S NO OVERFLOW PROTECTION IN NUMBER READ PROCEDURES!
+// read decimal number
+static char *getdec(const char *buf, int32_t *N){
+ int32_t num = 0;
+ int positive = TRUE;
+ if(*buf == '-'){
+ positive = FALSE;
+ ++buf;
+ }
+ while(*buf){
+ char c = *buf;
+ if(c < '0' || c > '9'){
+ break;
+ }
+ num *= 10;
+ num += c - '0';
+ ++buf;
+ }
+ *N = (positive) ? num : -num;
+ return (char *)buf;
+}
+// read hexadecimal number (without 0x prefix!)
+static char *gethex(const char *buf, int32_t *N){
+ uint32_t num = 0;
+ while(*buf){
+ char c = *buf;
+ uint8_t M = 0;
+ if(c >= '0' && c <= '9'){
+ M = '0';
+ }else if(c >= 'A' && c <= 'F'){
+ M = 'A' - 10;
+ }else if(c >= 'a' && c <= 'f'){
+ M = 'a' - 10;
+ }
+ if(M){
+ num <<= 4;
+ num += c - M;
+ }else{
+ break;
+ }
+ ++buf;
+ }
+ *N = (int32_t)num;
+ return (char *)buf;
+}
+// read binary number (without 0b prefix!)
+static char *getbin(const char *buf, int32_t *N){
+ uint32_t num = 0;
+ while(*buf){
+ char c = *buf;
+ if(c < '0' || c > '1'){
+ break;
+ }
+ num <<= 1;
+ if(c == '1') num |= 1;
+ ++buf;
+ }
+ *N = (int32_t)num;
+ return (char *)buf;
+}
+
+/**
+ * @brief getnum - read uint32_t from string (dec, hex or bin: 127, 0x7f, 0b1111111)
+ * @param buf - buffer with number and so on
+ * @param N - the number read
+ * @return pointer to first non-number symbol in buf (if it is == buf, there's no number)
+ */
+char *getnum(char *txt, int32_t *N){
+ if(*txt == '0'){
+ if(txt[1] == 'x' || txt[1] == 'X') return gethex(txt+2, N);
+ if(txt[1] == 'b' || txt[1] == 'B') return getbin(txt+2, N);
+ }
+ return getdec(txt, N);
+}
+
+// show message in debug mode
+void mesg(char *txt){
+ if(!debugmode) return;
+ addtobuf("[DBG] ");
+ addtobuf(txt);
+ bufputchar('\n');
+}
diff --git a/F0:F030,F042,F072/TSYS_controller/proto.h b/F0:F030,F042,F072/TSYS_controller/proto.h
new file mode 100644
index 0000000..48e544f
--- /dev/null
+++ b/F0:F030,F042,F072/TSYS_controller/proto.h
@@ -0,0 +1,51 @@
+/*
+ * geany_encoding=koi8-r
+ * proto.h
+ *
+ * Copyright 2018 Edward V. Emelianov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ */
+#pragma once
+#ifndef __PROTO_H__
+#define __PROTO_H__
+
+#include "stm32f0.h"
+#include "hardware.h"
+
+// macro for static strings
+#define SEND(str) do{addtobuf(str);}while(0)
+
+#ifdef EBUG
+#define MSG(str) do{addtobuf(__FILE__ " (L" STR(__LINE__) "): " str);}while(0)
+#else
+#define MSG(str)
+#endif
+
+#define newline() do{bufputchar('\n');}while(0)
+
+extern uint8_t noLED;
+void cmd_parser(char *buf, uint8_t isUSB);
+void addtobuf(const char *txt);
+void bufputchar(char ch);
+void printu(uint32_t val);
+void printuhex(uint32_t val);
+void sendbuf();
+char *getnum(char *txt, int32_t *N);
+void mesg(char *txt);
+
+#endif // __PROTO_H__
diff --git a/F0:F030,F042,F072/TSYS_controller/sensors_manage.c b/F0:F030,F042,F072/TSYS_controller/sensors_manage.c
new file mode 100644
index 0000000..5f50c6b
--- /dev/null
+++ b/F0:F030,F042,F072/TSYS_controller/sensors_manage.c
@@ -0,0 +1,442 @@
+/*
+ * geany_encoding=koi8-r
+ * sensors_manage.c
+ *
+ * Copyright 2018 Edward V. Emelianov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ */
+#include "sensors_manage.h"
+#include "can_process.h"
+#include "i2c.h"
+#include "proto.h" // addtobuf, bufputchar, memcpy
+
+extern volatile uint32_t Tms;
+uint8_t sensors_scan_mode = 0; // infinite scan mode
+static uint32_t lastSensT = 0;
+SensorsState Sstate = SENS_OFF; // turn on sensors only by request
+static uint8_t curr_mul_addr = 0; // current sensors pair address @ multiplexer
+static uint8_t overcurnt_ctr = 0; // if this counter > 32 go to OFF state
+uint8_t sens_present[2] = {0,0}; // bit flag: Nth bit == 1 if sensor[s] on given channel found
+uint8_t Nsens_present = 0; // total amount of sensors found
+uint8_t Ntemp_measured = 0; // total amount of themperatures measured
+
+// 8 - amount of pairs, 2 - amount in pair, 5 - amount of Coef.
+static uint16_t coefficients[MUL_MAX_ADDRESS+1][2][5]; // Coefficients for given sensors
+// measured temperatures * 100
+int16_t Temperatures[MUL_MAX_ADDRESS+1][2];
+
+// pair addresses
+static const uint8_t Taddr[2] = {TSYS01_ADDR0, TSYS01_ADDR1};
+
+static const char *statenames[] = {
+ [SENS_INITING] = "init"
+ ,[SENS_RESETING] = "reset"
+ ,[SENS_GET_COEFFS] = "getcoeff"
+ ,[SENS_SLEEPING] = "sleep"
+ ,[SENS_START_MSRMNT] = "startmeasure"
+ ,[SENS_WAITING] = "waitresults"
+ ,[SENS_GATHERING] = "collectdata"
+ ,[SENS_OFF] = "off"
+ ,[SENS_OVERCURNT] = "overcurrent"
+ ,[SENS_OVERCURNT_OFF] = "offbyovercurrent"
+};
+
+static uint8_t getcoeff(uint8_t i);
+
+const char *sensors_get_statename(SensorsState x){
+ if(x >= SENS_STATE_CNT) return "wrongstate";
+ return statenames[x];
+}
+
+// TODO: check if we can convert double to float!
+
+const double mul[5] = {-1.5e-2, 1., -2., 4., -2.};
+/**
+ * Get temperature & calculate it by polinome
+ * T = (-2) * k4 * 10^{-21} * ADC16^4
+ * + 4 * k3 * 10^{-16} * ADC16^3
+ * + (-2) * k2 * 10^{-11} * ADC16^2
+ * + 1 * k1 * 10^{-6} * ADC16
+ * +(-1.5)* k0 * 10^{-2}
+ * k0*(-1.5e-2) + 1e-6*val*(k1 + 1e-5*val*(-2*k2 + 1e-5*val*(4*k3 + -2e-5*k4*val)))
+ *
+ * @param t - value from sensor
+ * @param i - number of sensor in pair
+ * @return -30000 if something wrong or T*100 if all OK
+ */
+static uint16_t calc_t(uint32_t t, int i){
+ uint16_t *coeff = coefficients[curr_mul_addr][i];
+ if(coeff[0] == 0){
+ if(!getcoeff(i)) return BAD_TEMPERATURE; // what is with coeffs?
+ }
+ if(t < 600000 || t > 30000000) return BAD_TEMPERATURE; // wrong value - too small or too large
+ int j;
+ double d = (double)t/256., tmp = 0.;
+ // k0*(-1.5e-2) + 0.1*1e-5*val*(1*k1 + 1e-5*val*(-2.*k2 + 1e-5*val*(4*k3 + 1e-5*val*(-2*k4))))
+ for(j = 4; j > 0; --j){
+ tmp += mul[j] * (double)coeff[j];
+ tmp *= 1e-5*d;
+ }
+ tmp = tmp * 10. + 100. * mul[0] * coeff[0];
+ return (uint16_t)tmp;
+}
+
+// turn off sensors' power
+void sensors_off(){
+ mesg("Turn off sensors");
+ MUL_OFF(); // turn off multiplexers
+ SENSORS_OFF(); // turn off sensors' power
+ Sstate = SENS_OFF;
+}
+
+/**
+ * if all OK with current, turn ON sensors' power
+ */
+static int sensors_on(){
+ mesg("Turn on sensors");
+ curr_mul_addr = 0;
+ MUL_OFF();
+ if(SENSORS_OVERCURNT()){
+ mesg("OVERCURRENT!");
+ SENSORS_OFF();
+ Sstate = (++overcurnt_ctr > 32) ? SENS_OVERCURNT_OFF : SENS_OVERCURNT;
+ return FALSE;
+ }else{
+ mesg("Powered on");
+ SENSORS_ON();
+ return TRUE;
+ }
+}
+
+// init sensors
+void sensors_init(){
+ sens_present[0] = sens_present[1] = 0;
+ overcurnt_ctr = 0;
+ Nsens_present = 0;
+ if(sensors_on()) Sstate = SENS_INITING;
+}
+
+/**
+ * start measurement if sensors are sleeping,
+ * turn ON if they were OFF
+ * do nothing if measurement processing
+ */
+void sensors_start(){
+ if(sensors_scan_mode) return;
+ switch(Sstate){
+ case SENS_SLEEPING:
+ Sstate = SENS_START_MSRMNT;
+ break;
+ case SENS_OFF:
+ overcurnt_ctr = 0;
+ if(sensors_on()) Sstate = SENS_START_MSRMNT;
+ break;
+ case SENS_OVERCURNT_OFF:
+ sensors_init();
+ break;
+ default:
+ break;
+ }
+}
+
+// count 1 bits in sens_present & set `Nsens_present` to this value
+static void count_sensors(){
+ Nsens_present = 0;
+ uint16_t B = sens_present[0]<<8 | sens_present[1];
+ while(B){
+ ++Nsens_present;
+ B &= (B - 1);
+ }
+/*
+SEND("count_sensors(): ");
+printu(Nsens_present);
+newline();
+*/
+}
+
+/**
+ * All procedures return TRUE if all OK or FALSE if failed and need to start scan again
+ */
+// procedure call each time @ resetting
+static uint8_t resetproc(){
+ uint8_t i;
+ for(i = 0; i < 2; ++i){
+ if(write_i2c(Taddr[i], TSYS01_RESET)){
+ sens_present[i] |= 1< MUL_MAX_ADDRESS){ // scan is over
+ curr_mul_addr = 0;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+// print coefficients @debug console
+void showcoeffs(){
+ int a, p, k;
+ if(Nsens_present == 0){
+ SEND("showcoeffs(): no sensors found\n");
+ return;
+ }
+ for(a = 0; a <= MUL_MAX_ADDRESS; ++a){
+ for(p = 0; p < 2; ++p){
+ if(!(sens_present[p] & (1< 32) ? SENS_OVERCURNT_OFF : SENS_OVERCURNT;
+ return;
+ }
+ switch(Sstate){
+ case SENS_INITING: // initialisation (restart I2C)
+ mesg("SENS_INITING");
+ i2c_setup(CURRENT_SPEED);
+ Sstate = SENS_RESETING;
+ lastSensT = Tms;
+ NsentOverCAN = -1;
+ break;
+ case SENS_RESETING: // reset & discovery procedure
+ if(NsentOverCAN == -1){
+ mesg("SENS_RESETING");
+ NsentOverCAN = 0;
+ }
+ if(Tms - lastSensT > POWERUP_TIME){
+ if(sensors_scan(resetproc)){
+ count_sensors(); // get total amount of sensors
+ if(Nsens_present){
+ Sstate = SENS_GET_COEFFS;
+ }else{ // no sensors found
+ mesg("No sensors found -> off");
+ sensors_off();
+ }
+ }
+ }
+ break;
+ case SENS_GET_COEFFS: // get coefficients
+ mesg("SENS_GET_COEFFS");
+ if(sensors_scan(getcoefsproc)){
+ Sstate = SENS_SLEEPING; // sleep after got coefficients
+ }
+ break;
+ case SENS_START_MSRMNT: // send all sensors command to start measurements
+ mesg("SENS_START_MSRMNT");
+ if(sensors_scan(msrtempproc)){
+ lastSensT = Tms;
+ Sstate = SENS_WAITING;
+ Ntemp_measured = 0; // reset value of good measurements
+ }
+ break;
+ case SENS_WAITING: // wait for end of conversion
+ mesg("SENS_WAITING");
+ if(Tms - lastSensT > CONV_TIME){
+ NsentOverCAN = -1;
+ Sstate = SENS_GATHERING;
+ }
+ break;
+ case SENS_GATHERING: // scan all sensors, get thermal data & calculate temperature
+ if(NsentOverCAN < 0){
+ mesg("SENS_SLEEPING");
+ NsentOverCAN = 0;
+ }
+ if(sensors_scan(gettempproc)){
+ lastSensT = Tms;
+ NsentOverCAN = 0;
+ Sstate = SENS_SENDING_DATA;
+ }
+ break;
+ case SENS_SENDING_DATA:
+ mesg("SENS_SENDING_DATA");
+ NsentOverCAN = send_temperatures(NsentOverCAN); // call sending T process
+ if(NsentOverCAN < 0){ // all data sent -> sleep
+ Sstate = SENS_SLEEPING;
+ /*
+ if(Nsens_present != Ntemp_measured){ // restart sensors only after measurements sent
+ mesg("restart");
+ i2c_setup(CURRENT_SPEED);
+ sensors_on();
+ }*/
+ }
+ break;
+ case SENS_SLEEPING: // wait for `SLEEP_TIME` till next measurements in scan mode
+ if(NsentOverCAN < 0){
+ mesg("SENS_SLEEPING");
+ NsentOverCAN = 0;
+ }
+ if(sensors_scan_mode){ // sleep until next measurement start
+ if(Tms - lastSensT > SLEEP_TIME){
+ Sstate = SENS_START_MSRMNT;
+ }
+ }
+ break;
+ case SENS_OVERCURNT: // try to reinit all after overcurrent
+ mesg("SENS_OVERCURNT");
+ if(sensors_on()) Sstate = SENS_SLEEPING;
+ break;
+ default: // do nothing
+ break;
+ }
+}
diff --git a/F0:F030,F042,F072/TSYS_controller/sensors_manage.h b/F0:F030,F042,F072/TSYS_controller/sensors_manage.h
new file mode 100644
index 0000000..aea0f15
--- /dev/null
+++ b/F0:F030,F042,F072/TSYS_controller/sensors_manage.h
@@ -0,0 +1,69 @@
+/*
+ * geany_encoding=koi8-r
+ * sensors_manage.h
+ *
+ * Copyright 2018 Edward V. Emelianov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ */
+#pragma once
+#ifndef __SENSORS_MANAGE_H__
+#define __SENSORS_MANAGE_H__
+
+#include "hardware.h"
+
+// time for power up procedure (500ms)
+#define POWERUP_TIME (500)
+// time between readings in scan mode (15sec)
+#define SLEEP_TIME (15000)
+// error in measurement == -300degrC
+#define BAD_TEMPERATURE (-30000)
+// no sensor on given channel
+#define NO_SENSOR (-31000)
+
+typedef enum{
+ SENS_INITING // 0 power on
+ ,SENS_RESETING // 1 discovery sensors resetting them
+ ,SENS_GET_COEFFS // 2 get coefficients from all sensors
+ ,SENS_SLEEPING // 3 wait for a time to process measurements
+ ,SENS_START_MSRMNT // 4 send command 2 start measurement
+ ,SENS_WAITING // 5 wait for measurements end
+ ,SENS_GATHERING // 6 collect information
+ ,SENS_OFF // 7 sensors' power is off by external command
+ ,SENS_OVERCURNT // 8 overcurrent detected @ any stage
+ ,SENS_OVERCURNT_OFF // 9 sensors' power is off due to continuous overcurrent
+ ,SENS_SENDING_DATA // A send data over CAN bus
+ ,SENS_STATE_CNT
+} SensorsState;
+
+extern uint8_t sensors_scan_mode;
+extern int16_t Temperatures[MUL_MAX_ADDRESS+1][2];
+extern uint8_t sens_present[2];
+extern SensorsState Sstate;
+extern uint8_t Nsens_present;
+extern uint8_t Ntemp_measured;
+
+const char *sensors_get_statename(SensorsState x);
+void sensors_process();
+
+void sensors_off();
+void sensors_init();
+void sensors_start();
+void showcoeffs();
+void showtemperature();
+
+#endif // __SENSORS_MANAGE_H__
diff --git a/F0:F030,F042,F072/TSYS_controller/tsys01.bin b/F0:F030,F042,F072/TSYS_controller/tsys01.bin
new file mode 100755
index 0000000..d4fa982
Binary files /dev/null and b/F0:F030,F042,F072/TSYS_controller/tsys01.bin differ
diff --git a/F0:F030,F042,F072/TSYS_controller/usart.c b/F0:F030,F042,F072/TSYS_controller/usart.c
new file mode 100644
index 0000000..f149b12
--- /dev/null
+++ b/F0:F030,F042,F072/TSYS_controller/usart.c
@@ -0,0 +1,216 @@
+/*
+ * usart.c
+ *
+ * Copyright 2017 Edward V. Emelianoff
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+#include
+
+#include "stm32f0.h"
+#include "hardware.h"
+#include "usart.h"
+
+
+extern volatile uint32_t Tms;
+static int datalen[2] = {0,0}; // received data line length (including '\n')
+
+volatile int linerdy = 0, // received data ready
+ dlen = 0, // length of data (including '\n') in current buffer
+ bufovr = 0, // input buffer overfull
+ txrdy = 1 // transmission done
+;
+
+
+int rbufno = 0; // current rbuf number
+static char rbuf[UARTINBUFSZ][2], tbuf[UARTBUFSZ]; // receive & transmit buffers
+static char *recvdata = NULL;
+
+/**
+ * return length of received data (without trailing zero
+ */
+int usart_getline(char **line){
+ if(bufovr){
+ bufovr = 0;
+ linerdy = 0;
+ return 0;
+ }
+ *line = recvdata;
+ linerdy = 0;
+ return dlen;
+}
+
+TXstatus usart_send(const char *str, int len){
+ if(!txrdy) return LINE_BUSY;
+ if(len > UARTBUFSZ) return STR_TOO_LONG;
+ txrdy = 0;
+ memcpy(tbuf, str, len);
+#if USARTNUM == 2
+ DMA1_Channel4->CCR &= ~DMA_CCR_EN;
+ DMA1_Channel4->CNDTR = len;
+ DMA1_Channel4->CCR |= DMA_CCR_EN; // start transmission
+#elif USARTNUM == 1
+ DMA1_Channel2->CCR &= ~DMA_CCR_EN;
+ DMA1_Channel2->CNDTR = len;
+ DMA1_Channel2->CCR |= DMA_CCR_EN;
+#else
+#error "Not implemented"
+#endif
+ return ALL_OK;
+}
+
+TXstatus usart_send_blocking(const char *str, int len){
+ if(!txrdy) return LINE_BUSY;
+ int i;
+ bufovr = 0;
+ for(i = 0; i < len; ++i){
+ USARTX -> TDR = *str++;
+ while(!(USARTX->ISR & USART_ISR_TXE)){IWDG->KR = IWDG_REFRESH;};
+ }
+ return ALL_OK;
+}
+
+void usart_send_blck(const char *str){
+ while(!txrdy){IWDG->KR = IWDG_REFRESH;}
+ bufovr = 0;
+ while(*str){
+ USARTX -> TDR = *str++;
+ while(!(USARTX->ISR & USART_ISR_TXE)){IWDG->KR = IWDG_REFRESH;};
+ }
+}
+
+void usart_setup(){
+// Nucleo's USART2 connected to VCP proxy of st-link
+#if USARTNUM == 2
+ // setup pins: PA2 (Tx - AF1), PA15 (Rx - AF1)
+ // AF mode (AF1)
+ GPIOA->MODER = (GPIOA->MODER & ~(GPIO_MODER_MODER2|GPIO_MODER_MODER15))\
+ | (GPIO_MODER_MODER2_AF | GPIO_MODER_MODER15_AF);
+ GPIOA->AFR[0] = (GPIOA->AFR[0] &~GPIO_AFRH_AFRH2) | 1 << (2 * 4); // PA2
+ GPIOA->AFR[1] = (GPIOA->AFR[1] &~GPIO_AFRH_AFRH7) | 1 << (7 * 4); // PA15
+ // DMA: Tx - Ch4
+ DMA1_Channel4->CPAR = (uint32_t) &USART2->TDR; // periph
+ DMA1_Channel4->CMAR = (uint32_t) tbuf; // mem
+ DMA1_Channel4->CCR |= DMA_CCR_MINC | DMA_CCR_DIR | DMA_CCR_TCIE; // 8bit, mem++, mem->per, transcompl irq
+ // Tx CNDTR set @ each transmission due to data size
+ NVIC_SetPriority(DMA1_Channel4_5_IRQn, 3);
+ NVIC_EnableIRQ(DMA1_Channel4_5_IRQn);
+ NVIC_SetPriority(USART2_IRQn, 0);
+ // setup usart2
+ RCC->APB1ENR |= RCC_APB1ENR_USART2EN; // clock
+ // oversampling by16, 115200bps (fck=48mHz)
+ //USART2_BRR = 0x1a1; // 48000000 / 115200
+ USART2->BRR = 480000 / 1152;
+ USART2->CR3 = USART_CR3_DMAT; // enable DMA Tx
+ USART2->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE; // 1start,8data,nstop; enable Rx,Tx,USART
+ while(!(USART2->ISR & USART_ISR_TC)); // polling idle frame Transmission
+ USART2->ICR |= USART_ICR_TCCF; // clear TC flag
+ USART2->CR1 |= USART_CR1_RXNEIE;
+ NVIC_EnableIRQ(USART2_IRQn);
+// USART1 of main board
+#elif USARTNUM == 1
+ // PA9 - Tx, PA10 - Rx (AF1)
+ GPIOA->MODER = (GPIOA->MODER & ~(GPIO_MODER_MODER9 | GPIO_MODER_MODER10))\
+ | (GPIO_MODER_MODER9_AF | GPIO_MODER_MODER10_AF);
+ GPIOA->AFR[1] = (GPIOA->AFR[1] & ~(GPIO_AFRH_AFRH1 | GPIO_AFRH_AFRH2)) |
+ 1 << (1 * 4) | 1 << (2 * 4); // PA9, PA10
+ // USART1 Tx DMA - Channel2 (default value in SYSCFG_CFGR1)
+ DMA1_Channel2->CPAR = (uint32_t) &USART1->TDR; // periph
+ DMA1_Channel2->CMAR = (uint32_t) tbuf; // mem
+ DMA1_Channel2->CCR |= DMA_CCR_MINC | DMA_CCR_DIR | DMA_CCR_TCIE; // 8bit, mem++, mem->per, transcompl irq
+ // Tx CNDTR set @ each transmission due to data size
+ NVIC_SetPriority(DMA1_Channel2_3_IRQn, 3);
+ NVIC_EnableIRQ(DMA1_Channel2_3_IRQn);
+ NVIC_SetPriority(USART1_IRQn, 0);
+ // setup usart1
+ RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
+ USART1->BRR = 480000 / 1152;
+ USART1->CR3 = USART_CR3_DMAT; // enable DMA Tx
+ USART1->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE; // 1start,8data,nstop; enable Rx,Tx,USART
+ while(!(USART1->ISR & USART_ISR_TC)); // polling idle frame Transmission
+ USART1->ICR |= USART_ICR_TCCF; // clear TC flag
+ USART1->CR1 |= USART_CR1_RXNEIE;
+ NVIC_EnableIRQ(USART1_IRQn);
+#else
+#error "Not implemented"
+#endif
+}
+
+#if USARTNUM == 2
+void usart2_isr(){
+// USART1
+#elif USARTNUM == 1
+void usart1_isr(){
+#else
+#error "Not implemented"
+#endif
+ #ifdef CHECK_TMOUT
+ static uint32_t tmout = 0;
+ #endif
+ if(USARTX->ISR & USART_ISR_RXNE){ // RX not emty - receive next char
+ #ifdef CHECK_TMOUT
+ if(tmout && Tms >= tmout){ // set overflow flag
+ bufovr = 1;
+ datalen[rbufno] = 0;
+ }
+ tmout = Tms + TIMEOUT_MS;
+ if(!tmout) tmout = 1; // prevent 0
+ #endif
+ // read RDR clears flag
+ uint8_t rb = USARTX->RDR;
+ if(datalen[rbufno] < UARTINBUFSZ){ // put next char into buf
+ rbuf[rbufno][datalen[rbufno]++] = rb;
+ if(rb == '\n'){ // got newline - line ready
+ linerdy = 1;
+ dlen = datalen[rbufno];
+ recvdata = rbuf[rbufno];
+ // prepare other buffer
+ rbufno = !rbufno;
+ datalen[rbufno] = 0;
+ #ifdef CHECK_TMOUT
+ // clear timeout at line end
+ tmout = 0;
+ #endif
+ }
+ }else{ // buffer overrun
+ bufovr = 1;
+ datalen[rbufno] = 0;
+ #ifdef CHECK_TMOUT
+ tmout = 0;
+ #endif
+ }
+ }
+}
+
+#if USARTNUM == 2
+void dma1_channel4_5_isr(){
+ if(DMA1->ISR & DMA_ISR_TCIF4){ // Tx
+ DMA1->IFCR |= DMA_IFCR_CTCIF4; // clear TC flag
+ txrdy = 1;
+ }
+}
+// USART1
+#elif USARTNUM == 1
+void dma1_channel2_3_isr(){
+ if(DMA1->ISR & DMA_ISR_TCIF2){ // Tx
+ DMA1->IFCR |= DMA_IFCR_CTCIF2; // clear TC flag
+ txrdy = 1;
+ }
+}
+#else
+#error "Not implemented"
+#endif
diff --git a/F0:F030,F042,F072/TSYS_controller/usart.h b/F0:F030,F042,F072/TSYS_controller/usart.h
new file mode 100644
index 0000000..f9764cf
--- /dev/null
+++ b/F0:F030,F042,F072/TSYS_controller/usart.h
@@ -0,0 +1,51 @@
+/*
+ * usart.h
+ *
+ * Copyright 2017 Edward V. Emelianoff
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+#pragma once
+#ifndef __USART_H__
+#define __USART_H__
+
+// output buffers size
+#define UARTBUFSZ (512)
+// input buffer size
+#define UARTINBUFSZ (64)
+// timeout between data bytes
+#ifndef TIMEOUT_MS
+#define TIMEOUT_MS (1500)
+#endif
+
+typedef enum{
+ ALL_OK,
+ LINE_BUSY,
+ STR_TOO_LONG
+} TXstatus;
+
+#define usartrx() (linerdy)
+#define usartovr() (bufovr)
+
+extern volatile int linerdy, bufovr, txrdy;
+
+void usart_setup();
+int usart_getline(char **line);
+TXstatus usart_send(const char *str, int len);
+TXstatus usart_send_blocking(const char *str, int len);
+void usart_send_blck(const char *str);
+
+#endif // __USART_H__
diff --git a/F0:F030,F042,F072/TSYS_controller/usb.c b/F0:F030,F042,F072/TSYS_controller/usb.c
new file mode 100644
index 0000000..03cd614
--- /dev/null
+++ b/F0:F030,F042,F072/TSYS_controller/usb.c
@@ -0,0 +1,183 @@
+/*
+ * geany_encoding=koi8-r
+ * usb.c - base functions for different USB types
+ *
+ * Copyright 2018 Edward V. Emelianov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ */
+
+#include
+
+#include "usart.h"
+#include "usb.h"
+#include "usb_lib.h"
+
+// incoming buffer size
+#define IDATASZ (256)
+static uint8_t incoming_data[IDATASZ];
+static uint8_t ovfl = 0;
+static uint16_t idatalen = 0;
+static int8_t usbON = 0; // ==1 when USB fully configured
+static volatile uint8_t tx_succesfull = 0;
+
+// interrupt IN handler (never used?)
+static uint16_t EP1_Handler(ep_t ep){
+ uint8_t ep0buf[11];
+ if (ep.rx_flag){
+ EP_Read(1, ep0buf);
+ ep.status = SET_VALID_TX(ep.status);
+ ep.status = KEEP_STAT_RX(ep.status);
+ }else if (ep.tx_flag){
+ ep.status = SET_VALID_RX(ep.status);
+ ep.status = SET_STALL_TX(ep.status);
+ }
+ return ep.status;
+}
+
+// data IN/OUT handler
+static uint16_t EP23_Handler(ep_t ep){
+ if(ep.rx_flag){
+ int rd = ep.rx_cnt, rest = IDATASZ - idatalen;
+ if(rd){
+ if(rd <= rest){
+ idatalen += EP_Read(2, &incoming_data[idatalen]);
+ ovfl = 0;
+ }else{
+ ep.status = SET_NAK_RX(ep.status);
+ ovfl = 1;
+ return ep.status;
+ }
+ }
+ ep.status = CLEAR_DTOG_RX(ep.status);
+ ep.status = CLEAR_DTOG_TX(ep.status);
+ ep.status = SET_STALL_TX(ep.status);
+ }else if (ep.tx_flag){
+ ep.status = KEEP_STAT_TX(ep.status);
+ tx_succesfull = 1;
+ }
+ ep.status = SET_VALID_RX(ep.status);
+ return ep.status;
+}
+
+void USB_setup(){
+ RCC->APB1ENR |= RCC_APB1ENR_CRSEN | RCC_APB1ENR_USBEN; // enable CRS (hsi48 sync) & USB
+ RCC->CFGR3 &= ~RCC_CFGR3_USBSW; // reset USB
+ RCC->CR2 |= RCC_CR2_HSI48ON; // turn ON HSI48
+ uint32_t tmout = 16000000;
+ while(!(RCC->CR2 & RCC_CR2_HSI48RDY)){if(--tmout == 0) break; IWDG->KR = IWDG_REFRESH;}
+ FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY;
+ CRS->CFGR &= ~CRS_CFGR_SYNCSRC;
+ CRS->CFGR |= CRS_CFGR_SYNCSRC_1; // USB SOF selected as sync source
+ CRS->CR |= CRS_CR_AUTOTRIMEN; // enable auto trim
+ CRS->CR |= CRS_CR_CEN; // enable freq counter & block CRS->CFGR as read-only
+ RCC->CFGR |= RCC_CFGR_SW;
+ // allow RESET and CTRM interrupts
+ USB->CNTR = USB_CNTR_RESETM | USB_CNTR_CTRM;
+ // clear flags
+ USB->ISTR = 0;
+ // and activate pullup
+ USB->BCDR |= USB_BCDR_DPPU;
+ NVIC_EnableIRQ(USB_IRQn);
+}
+
+void usb_proc(){
+ if(USB_GetState() == USB_CONFIGURE_STATE){ // USB configured - activate other endpoints
+ if(!usbON){ // endpoints not activated
+ // make new BULK endpoint
+ // Buffer have 1024 bytes, but last 256 we use for CAN bus (30.2 of RM: USB main features)
+ EP_Init(1, EP_TYPE_INTERRUPT, 10, 0, EP1_Handler); // IN1 - transmit
+ EP_Init(2, EP_TYPE_BULK, 0, USB_RXBUFSZ, EP23_Handler); // OUT2 - receive data
+ EP_Init(3, EP_TYPE_BULK, USB_TXBUFSZ, 0, EP23_Handler); // IN3 - transmit data
+ usbON = 1;
+ }
+ }else{
+ usbON = 0;
+ }
+}
+
+void USB_send(const char *buf){
+ uint16_t l = 0, ctr = 0;
+ const char *p = buf;
+ while(*p++) ++l;
+ while(l){
+ IWDG->KR = IWDG_REFRESH;
+ uint16_t s = (l > USB_TXBUFSZ) ? USB_TXBUFSZ : l;
+ tx_succesfull = 0;
+ EP_Write(3, (uint8_t*)&buf[ctr], s);
+ uint32_t ctra = 1000000;
+ while(--ctra && tx_succesfull == 0){IWDG->KR = IWDG_REFRESH;}
+ l -= s;
+ ctr += s;
+ }
+}
+
+/**
+ * @brief USB_receive - read first received text string
+ * @param buf (i) - buffer for received data
+ * @param bufsize - its size
+ * @return amount of received bytes
+ */
+int USB_receive(char *buf, int bufsize){
+ if(bufsize<1 || !idatalen) return 0;
+ IWDG->KR = IWDG_REFRESH;
+ int stlen = 0, i;
+ for(i = 0; i < idatalen; ++i){
+ if(incoming_data[i] == '\n'){
+ stlen = i+1;
+ break;
+ }
+ }
+ if(i == idatalen || stlen == 0) return 0;
+ /*
+ char x[] = "USB got x:\n";
+ x[8] = '0' + stlen;
+ usart_send_blck(x);
+ usart_send_blck((char*)incoming_data);
+ usart_send_blck("\n");
+ */
+ USB->CNTR = 0;
+ int sz = (stlen > bufsize) ? bufsize : stlen, rest = idatalen - sz;
+ memcpy(buf, incoming_data, sz);
+ buf[sz] = 0;
+ /*
+ usart_send_blck("buf:\n");
+ usart_send_blck((char*)buf);
+ usart_send_blck("\n");
+ */
+ if(rest > 0){
+ memmove(incoming_data, &incoming_data[sz], rest);
+ idatalen = rest;
+ }else idatalen = 0;
+ if(ovfl){
+ EP23_Handler(endpoints[2]);
+ uint16_t epstatus = USB->EPnR[2];
+ epstatus = CLEAR_DTOG_RX(epstatus);
+ epstatus = SET_VALID_RX(epstatus);
+ USB->EPnR[2] = epstatus;
+ }
+ USB->CNTR = USB_CNTR_RESETM | USB_CNTR_CTRM;
+ return sz;
+}
+
+/**
+ * @brief USB_configured
+ * @return 1 if USB is in configured state
+ */
+int USB_configured(){
+ return usbON;
+}
diff --git a/F0:F030,F042,F072/TSYS_controller/usb.h b/F0:F030,F042,F072/TSYS_controller/usb.h
new file mode 100644
index 0000000..75d22b4
--- /dev/null
+++ b/F0:F030,F042,F072/TSYS_controller/usb.h
@@ -0,0 +1,37 @@
+/*
+ * geany_encoding=koi8-r
+ * usb.h
+ *
+ * Copyright 2018 Edward V. Emelianov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ */
+#pragma once
+#ifndef __USB_H__
+#define __USB_H__
+
+#include "hardware.h"
+
+#define BUFFSIZE (64)
+
+void USB_setup();
+void usb_proc();
+void USB_send(const char *buf);
+int USB_receive(char *buf, int bufsize);
+int USB_configured();
+
+#endif // __USB_H__
diff --git a/F0:F030,F042,F072/TSYS_controller/usb_defs.h b/F0:F030,F042,F072/TSYS_controller/usb_defs.h
new file mode 100644
index 0000000..6ac673c
--- /dev/null
+++ b/F0:F030,F042,F072/TSYS_controller/usb_defs.h
@@ -0,0 +1,106 @@
+/*
+ * geany_encoding=koi8-r
+ * usb_defs.h
+ *
+ * Copyright 2018 Edward V. Emelianov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ */
+
+#pragma once
+#ifndef __USB_DEFS_H__
+#define __USB_DEFS_H__
+
+#include
+
+/**
+ * Buffers size definition
+ **/
+// !!! when working with CAN bus change USB_BTABLE_SIZE to 768 !!!
+#define USB_BTABLE_SIZE 1024
+// first 64 bytes of USB_BTABLE are registers!
+#define USB_EP0_BASEADDR 64
+// for USB FS EP0 buffers are from 8 to 64 bytes long (64 for PL2303)
+#define USB_EP0_BUFSZ 64
+// USB transmit buffer size (64 for PL2303)
+#define USB_TXBUFSZ 64
+// USB receive buffer size (64 for PL2303)
+#define USB_RXBUFSZ 64
+
+#define USB_BTABLE_BASE 0x40006000
+#undef USB_BTABLE
+#define USB_BTABLE ((USB_BtableDef *)(USB_BTABLE_BASE))
+#define USB_ISTR_EPID 0x0000000F
+#define USB_FNR_LSOF_0 0x00000800
+#define USB_FNR_lSOF_1 0x00001000
+#define USB_LPMCSR_BESL_0 0x00000010
+#define USB_LPMCSR_BESL_1 0x00000020
+#define USB_LPMCSR_BESL_2 0x00000040
+#define USB_LPMCSR_BESL_3 0x00000080
+#define USB_EPnR_CTR_RX 0x00008000
+#define USB_EPnR_DTOG_RX 0x00004000
+#define USB_EPnR_STAT_RX 0x00003000
+#define USB_EPnR_STAT_RX_0 0x00001000
+#define USB_EPnR_STAT_RX_1 0x00002000
+#define USB_EPnR_SETUP 0x00000800
+#define USB_EPnR_EP_TYPE 0x00000600
+#define USB_EPnR_EP_TYPE_0 0x00000200
+#define USB_EPnR_EP_TYPE_1 0x00000400
+#define USB_EPnR_EP_KIND 0x00000100
+#define USB_EPnR_CTR_TX 0x00000080
+#define USB_EPnR_DTOG_TX 0x00000040
+#define USB_EPnR_STAT_TX 0x00000030
+#define USB_EPnR_STAT_TX_0 0x00000010
+#define USB_EPnR_STAT_TX_1 0x00000020
+#define USB_EPnR_EA 0x0000000F
+#define USB_COUNTn_RX_BLSIZE 0x00008000
+#define USB_COUNTn_NUM_BLOCK 0x00007C00
+#define USB_COUNTn_RX 0x0000003F
+
+#define USB_TypeDef USB_TypeDef_custom
+
+typedef struct{
+ __IO uint32_t EPnR[8];
+ __IO uint32_t RESERVED1;
+ __IO uint32_t RESERVED2;
+ __IO uint32_t RESERVED3;
+ __IO uint32_t RESERVED4;
+ __IO uint32_t RESERVED5;
+ __IO uint32_t RESERVED6;
+ __IO uint32_t RESERVED7;
+ __IO uint32_t RESERVED8;
+ __IO uint32_t CNTR;
+ __IO uint32_t ISTR;
+ __IO uint32_t FNR;
+ __IO uint32_t DADDR;
+ __IO uint32_t BTABLE;
+ __IO uint32_t LPMCSR;
+ __IO uint32_t BCDR;
+} USB_TypeDef;
+
+typedef struct{
+ __IO uint16_t USB_ADDR_TX;
+ __IO uint16_t USB_COUNT_TX;
+ __IO uint16_t USB_ADDR_RX;
+ __IO uint16_t USB_COUNT_RX;
+} USB_EPDATA_TypeDef;
+
+typedef struct{
+ __IO USB_EPDATA_TypeDef EP[8];
+} USB_BtableDef;
+
+#endif // __USB_DEFS_H__
diff --git a/F0:F030,F042,F072/TSYS_controller/usb_lib.c b/F0:F030,F042,F072/TSYS_controller/usb_lib.c
new file mode 100644
index 0000000..4b1cbcb
--- /dev/null
+++ b/F0:F030,F042,F072/TSYS_controller/usb_lib.c
@@ -0,0 +1,538 @@
+/*
+ * geany_encoding=koi8-r
+ * usb_lib.c
+ *
+ * Copyright 2018 Edward V. Emelianov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ */
+
+#include // memcpy
+
+#include "stm32f0.h"
+#include "usart.h"
+#include "usb_lib.h"
+
+
+#ifdef EBUG
+#undef EBUG
+#endif
+
+ep_t endpoints[ENDPOINTS_NUM];
+
+static usb_dev_t USB_Dev;
+static usb_LineCoding lineCoding = {115200, 0, 0, 8};
+static config_pack_t setup_packet;
+static uint8_t ep0databuf[EP0DATABUF_SIZE];
+static uint8_t ep0dbuflen = 0;
+
+usb_LineCoding getLineCoding(){return lineCoding;}
+
+// definition of parts common for USB_DeviceDescriptor & USB_DeviceQualifierDescriptor
+#define bcdUSB_L 0x10
+#define bcdUSB_H 0x01
+#define bDeviceClass 0
+#define bDeviceSubClass 0
+#define bDeviceProtocol 0
+#define bNumConfigurations 1
+
+static const uint8_t USB_DeviceDescriptor[] = {
+ 18, // bLength
+ 0x01, // bDescriptorType - Device descriptor
+ bcdUSB_L, // bcdUSB_L - 1.10
+ bcdUSB_H, // bcdUSB_H
+ bDeviceClass, // bDeviceClass - USB_COMM
+ bDeviceSubClass, // bDeviceSubClass
+ bDeviceProtocol, // bDeviceProtocol
+ USB_EP0_BUFSZ, // bMaxPacketSize
+ 0x7b, // idVendor_L PL2303: VID=0x067b, PID=0x2303
+ 0x06, // idVendor_H
+ 0x03, // idProduct_L
+ 0x23, // idProduct_H
+ 0x00, // bcdDevice_Ver_L
+ 0x03, // bcdDevice_Ver_H
+ 0x01, // iManufacturer
+ 0x02, // iProduct
+ 0x00, // iSerialNumber
+ bNumConfigurations // bNumConfigurations
+};
+
+static const uint8_t USB_DeviceQualifierDescriptor[] = {
+ 10, //bLength
+ 0x06, // bDescriptorType - Device qualifier
+ bcdUSB_L, // bcdUSB_L
+ bcdUSB_H, // bcdUSB_H
+ bDeviceClass, // bDeviceClass
+ bDeviceSubClass, // bDeviceSubClass
+ bDeviceProtocol, // bDeviceProtocol
+ USB_EP0_BUFSZ, // bMaxPacketSize0
+ bNumConfigurations, // bNumConfigurations
+ 0x00 // Reserved
+};
+
+static const uint8_t USB_ConfigDescriptor[] = {
+ /*Configuration Descriptor*/
+ 0x09, /* bLength: Configuration Descriptor size */
+ 0x02, /* bDescriptorType: Configuration */
+ 39, /* wTotalLength:no of returned bytes */
+ 0x00,
+ 0x01, /* bNumInterfaces: 1 interface */
+ 0x01, /* bConfigurationValue: Configuration value */
+ 0x00, /* iConfiguration: Index of string descriptor describing the configuration */
+ 0xa0, /* bmAttributes - Bus powered, Remote wakeup */
+ 0x32, /* MaxPower 100 mA */
+
+ /*---------------------------------------------------------------------------*/
+
+ /*Interface Descriptor */
+ 0x09, /* bLength: Interface Descriptor size */
+ 0x04, /* bDescriptorType: Interface */
+ 0x00, /* bInterfaceNumber: Number of Interface */
+ 0x00, /* bAlternateSetting: Alternate setting */
+ 0x03, /* bNumEndpoints: 3 endpoints used */
+ 0xff, /* bInterfaceClass */
+ 0x00, /* bInterfaceSubClass */
+ 0x00, /* bInterfaceProtocol */
+ 0x00, /* iInterface: */
+///////////////////////////////////////////////////
+ /*Endpoint 1 Descriptor*/
+ 0x07, /* bLength: Endpoint Descriptor size */
+ 0x05, /* bDescriptorType: Endpoint */
+ 0x81, /* bEndpointAddress IN1 */
+ 0x03, /* bmAttributes: Interrupt */
+ 0x0a, /* wMaxPacketSize LO: */
+ 0x00, /* wMaxPacketSize HI: */
+ 0x01, /* bInterval: */
+
+ /*Endpoint OUT2 Descriptor*/
+ 0x07, /* bLength: Endpoint Descriptor size */
+ 0x05, /* bDescriptorType: Endpoint */
+ 0x02, /* bEndpointAddress: OUT2 */
+ 0x02, /* bmAttributes: Bulk */
+ (USB_RXBUFSZ & 0xff), /* wMaxPacketSize: 64 */
+ (USB_RXBUFSZ >> 8),
+ 0x00, /* bInterval: ignore for Bulk transfer */
+
+ /*Endpoint IN3 Descriptor*/
+ 0x07, /* bLength: Endpoint Descriptor size */
+ 0x05, /* bDescriptorType: Endpoint */
+ 0x83, /* bEndpointAddress IN3 */
+ 0x02, /* bmAttributes: Bulk */
+ (USB_TXBUFSZ & 0xff), /* wMaxPacketSize: 64 */
+ (USB_TXBUFSZ >> 8),
+ 0x00, /* bInterval: ignore for Bulk transfer */
+};
+
+_USB_LANG_ID_(USB_StringLangDescriptor, LANG_US);
+// these descriptors are not used in PL2303 emulator!
+_USB_STRING_(USB_StringSerialDescriptor, u"0");
+_USB_STRING_(USB_StringManufacturingDescriptor, u"Prolific Technology Inc.");
+_USB_STRING_(USB_StringProdDescriptor, u"USB-Serial Controller");
+
+/*
+ * default handlers
+ */
+// SET_LINE_CODING
+void WEAK linecoding_handler(usb_LineCoding __attribute__((unused)) *lc){
+ //MSG("linecoding_handler\n");
+}
+
+// SET_CONTROL_LINE_STATE
+void WEAK clstate_handler(uint16_t __attribute__((unused)) val){
+ //MSG("clstate_handler\n");
+}
+
+// SEND_BREAK
+void WEAK break_handler(){
+ //MSG("break_handler\n");
+}
+
+// handler of vendor requests
+void WEAK vendor_handler(config_pack_t *packet){
+ if(packet->bmRequestType & 0x80){ // read
+ //SEND("Read");
+ uint8_t c;
+ switch(packet->wValue){
+ case 0x8484:
+ c = 2;
+ break;
+ case 0x0080:
+ c = 1;
+ break;
+ case 0x8686:
+ c = 0xaa;
+ break;
+ default:
+ c = 0;
+ }
+ EP_WriteIRQ(0, &c, 1);
+ }else{ // write ZLP
+ //SEND("Write");
+ EP_WriteIRQ(0, (uint8_t *)0, 0);
+ }
+ /*SEND(" vendor, reqt=");
+ printuhex(packet->bmRequestType);
+ SEND(", wval=");
+ printuhex(packet->wValue);
+ usart_putchar('\n');*/
+}
+
+
+#ifdef EBUG
+ uint8_t _2wr = 0;
+ #define WRITEDUMP(str) do{MSG(str); _2wr = 1;}while(0)
+#else
+ #define WRITEDUMP(str)
+#endif
+static void wr0(const uint8_t *buf, uint16_t size){
+ if(setup_packet.wLength < size) size = setup_packet.wLength;
+ EP_WriteIRQ(0, buf, size);
+}
+
+static inline void get_descriptor(){
+ switch(setup_packet.wValue){
+ case DEVICE_DESCRIPTOR:
+ wr0(USB_DeviceDescriptor, sizeof(USB_DeviceDescriptor));
+ break;
+ case CONFIGURATION_DESCRIPTOR:
+ wr0(USB_ConfigDescriptor, sizeof(USB_ConfigDescriptor));
+ break;
+ case STRING_LANG_DESCRIPTOR:
+ wr0((const uint8_t *)&USB_StringLangDescriptor, STRING_LANG_DESCRIPTOR_SIZE_BYTE);
+ break;
+ case STRING_MAN_DESCRIPTOR:
+ wr0((const uint8_t *)&USB_StringManufacturingDescriptor, USB_StringManufacturingDescriptor.bLength);
+ break;
+ case STRING_PROD_DESCRIPTOR:
+ wr0((const uint8_t *)&USB_StringProdDescriptor, USB_StringProdDescriptor.bLength);
+ break;
+ case STRING_SN_DESCRIPTOR:
+ wr0((const uint8_t *)&USB_StringSerialDescriptor, USB_StringSerialDescriptor.bLength);
+ break;
+ case DEVICE_QUALIFIER_DESCRIPTOR:
+ wr0(USB_DeviceQualifierDescriptor, USB_DeviceQualifierDescriptor[0]);
+ break;
+ default:
+ WRITEDUMP("UNK_DES");
+ break;
+ }
+}
+
+static uint8_t configuration = 0; // reply for GET_CONFIGURATION (==1 if configured)
+static inline void std_d2h_req(){
+ uint16_t status = 0; // bus powered
+ switch(setup_packet.bRequest){
+ case GET_DESCRIPTOR:
+ get_descriptor();
+ break;
+ case GET_STATUS:
+ EP_WriteIRQ(0, (uint8_t *)&status, 2); // send status: Bus Powered
+ break;
+ case GET_CONFIGURATION:
+ WRITEDUMP("GET_CONFIGURATION");
+ EP_WriteIRQ(0, &configuration, 1);
+ break;
+ default:
+ WRITEDUMP("80:WR_REQ");
+ break;
+ }
+}
+
+static inline void std_h2d_req(){
+ switch(setup_packet.bRequest){
+ case SET_ADDRESS:
+ // new address will be assigned later - after acknowlegement or request to host
+ USB_Dev.USB_Addr = setup_packet.wValue;
+ break;
+ case SET_CONFIGURATION:
+ // Now device configured
+ USB_Dev.USB_Status = USB_CONFIGURE_STATE;
+ configuration = setup_packet.wValue;
+ break;
+ default:
+ WRITEDUMP("0:WR_REQ");
+ break;
+ }
+}
+
+/*
+bmRequestType: 76543210
+7 direction: 0 - host->device, 1 - device->host
+65 type: 0 - standard, 1 - class, 2 - vendor
+4..0 getter: 0 - device, 1 - interface, 2 - endpoint, 3 - other
+*/
+/**
+ * Endpoint0 (control) handler
+ * @param ep - endpoint state
+ * @return data written to EP0R
+ */
+static uint16_t EP0_Handler(ep_t ep){
+ uint16_t epstatus = ep.status; // EP0R on input -> return this value after modifications
+ uint8_t reqtype = setup_packet.bmRequestType & 0x7f;
+ uint8_t dev2host = (setup_packet.bmRequestType & 0x80) ? 1 : 0;
+ if ((ep.rx_flag) && (ep.setup_flag)){
+ switch(reqtype){
+ case STANDARD_DEVICE_REQUEST_TYPE: // standard device request
+ if(dev2host){
+ std_d2h_req();
+ }else{
+ std_h2d_req();
+ // send ZLP
+ EP_WriteIRQ(0, (uint8_t *)0, 0);
+ }
+ epstatus = SET_NAK_RX(epstatus);
+ epstatus = SET_VALID_TX(epstatus);
+ break;
+ case STANDARD_ENDPOINT_REQUEST_TYPE: // standard endpoint request
+ if (setup_packet.bRequest == CLEAR_FEATURE){
+ // send ZLP
+ EP_WriteIRQ(0, (uint8_t *)0, 0);
+ epstatus = SET_NAK_RX(epstatus);
+ epstatus = SET_VALID_TX(epstatus);
+ }else{
+ WRITEDUMP("02:WR_REQ");
+ }
+ break;
+ case VENDOR_REQUEST_TYPE:
+ vendor_handler(&setup_packet);
+ epstatus = SET_NAK_RX(epstatus);
+ epstatus = SET_VALID_TX(epstatus);
+ break;
+ case CONTROL_REQUEST_TYPE:
+ switch(setup_packet.bRequest){
+ case GET_LINE_CODING:
+ EP_WriteIRQ(0, (uint8_t*)&lineCoding, sizeof(lineCoding));
+ break;
+ case SET_LINE_CODING: // omit this for next stage, when data will come
+ break;
+ case SET_CONTROL_LINE_STATE:
+ clstate_handler(setup_packet.wValue);
+ break;
+ case SEND_BREAK:
+ break_handler();
+ break;
+ default:
+ WRITEDUMP("undef control req");
+ }
+ if(!dev2host) EP_WriteIRQ(0, (uint8_t *)0, 0); // write acknowledgement
+ epstatus = SET_VALID_RX(epstatus);
+ epstatus = SET_VALID_TX(epstatus);
+ break;
+ default:
+ EP_WriteIRQ(0, (uint8_t *)0, 0);
+ epstatus = SET_NAK_RX(epstatus);
+ epstatus = SET_VALID_TX(epstatus);
+ }
+ }else if (ep.rx_flag){ // got data over EP0 or host acknowlegement
+ if(ep.rx_cnt){
+ EP_WriteIRQ(0, (uint8_t *)0, 0);
+ if(setup_packet.bRequest == SET_LINE_CODING){
+ //WRITEDUMP("SET_LINE_CODING");
+ linecoding_handler((usb_LineCoding*)ep0databuf);
+ }
+ }
+ // Close transaction
+ epstatus = CLEAR_DTOG_RX(epstatus);
+ epstatus = CLEAR_DTOG_TX(epstatus);
+ // wait for new data from host
+ epstatus = SET_VALID_RX(epstatus);
+ epstatus = SET_STALL_TX(epstatus);
+ } else if (ep.tx_flag){ // package transmitted
+ // now we can change address after enumeration
+ if ((USB->DADDR & USB_DADDR_ADD) != USB_Dev.USB_Addr){
+ USB->DADDR = USB_DADDR_EF | USB_Dev.USB_Addr;
+ // change state to ADRESSED
+ USB_Dev.USB_Status = USB_ADRESSED_STATE;
+ }
+ // end of transaction
+ epstatus = CLEAR_DTOG_RX(epstatus);
+ epstatus = CLEAR_DTOG_TX(epstatus);
+ epstatus = SET_VALID_RX(epstatus);
+ epstatus = SET_VALID_TX(epstatus);
+ }
+#ifdef EBUG
+ if(_2wr){
+ usart_putchar(' ');
+ if (ep.rx_flag) usart_putchar('r');
+ else usart_putchar('t');
+ printu(setup_packet.wLength);
+ if(ep.setup_flag) usart_putchar('s');
+ usart_putchar(' ');
+ usart_putchar('I');
+ printu(setup_packet.wIndex);
+ usart_putchar('V');
+ printu(setup_packet.wValue);
+ usart_putchar('R');
+ printu(setup_packet.bRequest);
+ usart_putchar('T');
+ printu(setup_packet.bmRequestType);
+ usart_putchar(' ');
+ usart_putchar('0' + ep0dbuflen);
+ usart_putchar(' ');
+ hexdump(ep0databuf, ep0dbuflen);
+ usart_putchar('\n');
+ }
+#endif
+ return epstatus;
+}
+#undef WRITEDUMP
+
+static uint16_t lastaddr = USB_EP0_BASEADDR;
+/**
+ * Endpoint initialisation
+ * !!! when working with CAN bus change USB_BTABLE_SIZE to 768 !!!
+ * @param number - EP num (0...7)
+ * @param type - EP type (EP_TYPE_BULK, EP_TYPE_CONTROL, EP_TYPE_ISO, EP_TYPE_INTERRUPT)
+ * @param txsz - transmission buffer size @ USB/CAN buffer
+ * @param rxsz - reception buffer size @ USB/CAN buffer
+ * @param uint16_t (*func)(ep_t *ep) - EP handler function
+ * @return 0 if all OK
+ */
+int EP_Init(uint8_t number, uint8_t type, uint16_t txsz, uint16_t rxsz, uint16_t (*func)(ep_t ep)){
+ if(number >= ENDPOINTS_NUM) return 4; // out of configured amount
+ if(txsz > USB_BTABLE_SIZE || rxsz > USB_BTABLE_SIZE) return 1; // buffer too large
+ if(lastaddr + txsz + rxsz >= USB_BTABLE_SIZE) return 2; // out of btable
+ USB->EPnR[number] = (type << 9) | (number & USB_EPnR_EA);
+ USB->EPnR[number] ^= USB_EPnR_STAT_RX | USB_EPnR_STAT_TX_1;
+ if(rxsz & 1 || rxsz > 992) return 3; // wrong rx buffer size
+ uint16_t countrx = 0;
+ if(rxsz < 64) countrx = rxsz / 2;
+ else{
+ if(rxsz & 0x1f) return 3; // should be multiple of 32
+ countrx = 31 + rxsz / 32;
+ }
+ USB_BTABLE->EP[number].USB_ADDR_TX = lastaddr;
+ endpoints[number].tx_buf = (uint16_t *)(USB_BTABLE_BASE + lastaddr);
+ lastaddr += txsz;
+ USB_BTABLE->EP[number].USB_COUNT_TX = 0;
+ USB_BTABLE->EP[number].USB_ADDR_RX = lastaddr;
+ endpoints[number].rx_buf = (uint8_t *)(USB_BTABLE_BASE + lastaddr);
+ lastaddr += rxsz;
+ // buffer size: Table127 of RM
+ USB_BTABLE->EP[number].USB_COUNT_RX = countrx << 10;
+ endpoints[number].func = func;
+ return 0;
+}
+
+// standard IRQ handler
+void usb_isr(){
+ if (USB->ISTR & USB_ISTR_RESET){
+ // Reinit registers
+ USB->CNTR = USB_CNTR_RESETM | USB_CNTR_CTRM;
+ USB->ISTR = 0;
+ // Endpoint 0 - CONTROL
+ // ON USB LS size of EP0 may be 8 bytes, but on FS it should be 64 bytes!
+ lastaddr = USB_EP0_BASEADDR; // roll back to beginning of buffer
+ EP_Init(0, EP_TYPE_CONTROL, USB_EP0_BUFSZ, USB_EP0_BUFSZ, EP0_Handler);
+ // clear address, leave only enable bit
+ USB->DADDR = USB_DADDR_EF;
+ // state is default - wait for enumeration
+ USB_Dev.USB_Status = USB_DEFAULT_STATE;
+ }
+ if(USB->ISTR & USB_ISTR_CTR){
+ // EP number
+ uint8_t n = USB->ISTR & USB_ISTR_EPID;
+ // copy status register
+ uint16_t epstatus = USB->EPnR[n];
+ // Calculate flags
+ endpoints[n].rx_flag = (epstatus & USB_EPnR_CTR_RX) ? 1 : 0;
+ endpoints[n].setup_flag = (epstatus & USB_EPnR_SETUP) ? 1 : 0;
+ endpoints[n].tx_flag = (epstatus & USB_EPnR_CTR_TX) ? 1 : 0;
+ // copy received bytes amount
+ endpoints[n].rx_cnt = USB_BTABLE->EP[n].USB_COUNT_RX & 0x3FF; // low 10 bits is counter
+ // check direction
+ if(USB->ISTR & USB_ISTR_DIR){ // OUT interrupt - receive data, CTR_RX==1 (if CTR_TX == 1 - two pending transactions: receive following by transmit)
+ if(n == 0){ // control endpoint
+ if(epstatus & USB_EPnR_SETUP){ // setup packet -> copy data to conf_pack
+ memcpy(&setup_packet, endpoints[0].rx_buf, sizeof(setup_packet));
+ ep0dbuflen = 0;
+ // interrupt handler will be called later
+ }else if(epstatus & USB_EPnR_CTR_RX){ // data packet -> push received data to ep0databuf
+ ep0dbuflen = endpoints[0].rx_cnt;
+ memcpy(ep0databuf, endpoints[0].rx_buf, ep0dbuflen);
+ }
+ }
+ }else{ // IN interrupt - transmit data, only CTR_TX == 1
+ // enumeration end could be here (if EP0)
+ }
+ // prepare status field for EP handler
+ endpoints[n].status = epstatus;
+ // call EP handler (even if it will change EPnR, it should return new status)
+ epstatus = endpoints[n].func(endpoints[n]);
+ // keep DTOG state
+ epstatus = KEEP_DTOG_TX(epstatus);
+ epstatus = KEEP_DTOG_RX(epstatus);
+ // clear all RX/TX flags
+ epstatus = CLEAR_CTR_RX(epstatus);
+ epstatus = CLEAR_CTR_TX(epstatus);
+ // refresh EPnR
+ USB->EPnR[n] = epstatus;
+ }
+}
+
+/**
+ * Write data to EP buffer (called from IRQ handler)
+ * @param number - EP number
+ * @param *buf - array with data
+ * @param size - its size
+ */
+void EP_WriteIRQ(uint8_t number, const uint8_t *buf, uint16_t size){
+ uint8_t i;
+ if(size > USB_TXBUFSZ) size = USB_TXBUFSZ;
+ uint16_t N2 = (size + 1) >> 1;
+ // the buffer is 16-bit, so we should copy data as it would be uint16_t
+ uint16_t *buf16 = (uint16_t *)buf;
+ for (i = 0; i < N2; i++){
+ endpoints[number].tx_buf[i] = buf16[i];
+ }
+ USB_BTABLE->EP[number].USB_COUNT_TX = size;
+}
+
+/**
+ * Write data to EP buffer (called outside IRQ handler)
+ * @param number - EP number
+ * @param *buf - array with data
+ * @param size - its size
+ */
+void EP_Write(uint8_t number, const uint8_t *buf, uint16_t size){
+ uint16_t status = USB->EPnR[number];
+ EP_WriteIRQ(number, buf, size);
+ status = SET_NAK_RX(status);
+ status = SET_VALID_TX(status);
+ status = KEEP_DTOG_TX(status);
+ status = KEEP_DTOG_RX(status);
+ USB->EPnR[number] = status;
+}
+
+/*
+ * Copy data from EP buffer into user buffer area
+ * @param *buf - user array for data
+ * @return amount of data read
+ */
+int EP_Read(uint8_t number, uint8_t *buf){
+ int n = endpoints[number].rx_cnt;
+ if(n){
+ for(int i = 0; i < n; ++i)
+ buf[i] = endpoints[number].rx_buf[i];
+ }
+ return n;
+}
+
+// USB status
+uint8_t USB_GetState(){
+ return USB_Dev.USB_Status;
+}
diff --git a/F0:F030,F042,F072/TSYS_controller/usb_lib.h b/F0:F030,F042,F072/TSYS_controller/usb_lib.h
new file mode 100644
index 0000000..0651e5f
--- /dev/null
+++ b/F0:F030,F042,F072/TSYS_controller/usb_lib.h
@@ -0,0 +1,202 @@
+/*
+ * geany_encoding=koi8-r
+ * usb_lib.h
+ *
+ * Copyright 2018 Edward V. Emelianov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ */
+
+#pragma once
+#ifndef __USB_LIB_H__
+#define __USB_LIB_H__
+
+#include
+#include "usb_defs.h"
+
+#define EP0DATABUF_SIZE (64)
+
+// Max EP amount (EP0 + other used)
+#define ENDPOINTS_NUM 4
+// bmRequestType & 0x7f
+#define STANDARD_DEVICE_REQUEST_TYPE 0
+#define STANDARD_ENDPOINT_REQUEST_TYPE 2
+#define VENDOR_REQUEST_TYPE 0x40
+#define CONTROL_REQUEST_TYPE 0x21
+// bRequest, standard; for bmRequestType == 0x80
+#define GET_STATUS 0x00
+#define GET_DESCRIPTOR 0x06
+#define GET_CONFIGURATION 0x08
+// for bmRequestType == 0
+#define CLEAR_FEATURE 0x01
+#define SET_FEATURE 0x03 // unused
+#define SET_ADDRESS 0x05
+#define SET_DESCRIPTOR 0x07 // unused
+#define SET_CONFIGURATION 0x09
+// for bmRequestType == 0x81, 1 or 0xB2
+#define GET_INTERFACE 0x0A // unused
+#define SET_INTERFACE 0x0B // unused
+#define SYNC_FRAME 0x0C // unused
+#define VENDOR_REQUEST 0x01 // unused
+
+// Class-Specific Control Requests
+#define SEND_ENCAPSULATED_COMMAND 0x00 // unused
+#define GET_ENCAPSULATED_RESPONSE 0x01 // unused
+#define SET_COMM_FEATURE 0x02 // unused
+#define GET_COMM_FEATURE 0x03 // unused
+#define CLEAR_COMM_FEATURE 0x04 // unused
+#define SET_LINE_CODING 0x20
+#define GET_LINE_CODING 0x21
+#define SET_CONTROL_LINE_STATE 0x22
+#define SEND_BREAK 0x23
+
+// control line states
+#define CONTROL_DTR 0x01
+#define CONTROL_RTS 0x02
+
+// wValue
+#define DEVICE_DESCRIPTOR 0x100
+#define CONFIGURATION_DESCRIPTOR 0x200
+#define STRING_LANG_DESCRIPTOR 0x300
+#define STRING_MAN_DESCRIPTOR 0x301
+#define STRING_PROD_DESCRIPTOR 0x302
+#define STRING_SN_DESCRIPTOR 0x303
+#define DEVICE_QUALIFIER_DESCRIPTOR 0x600
+
+// EPnR bits manipulation
+#define CLEAR_DTOG_RX(R) (R & USB_EPnR_DTOG_RX) ? R : (R & (~USB_EPnR_DTOG_RX))
+#define SET_DTOG_RX(R) (R & USB_EPnR_DTOG_RX) ? (R & (~USB_EPnR_DTOG_RX)) : R
+#define TOGGLE_DTOG_RX(R) (R | USB_EPnR_DTOG_RX)
+#define KEEP_DTOG_RX(R) (R & (~USB_EPnR_DTOG_RX))
+#define CLEAR_DTOG_TX(R) (R & USB_EPnR_DTOG_TX) ? R : (R & (~USB_EPnR_DTOG_TX))
+#define SET_DTOG_TX(R) (R & USB_EPnR_DTOG_TX) ? (R & (~USB_EPnR_DTOG_TX)) : R
+#define TOGGLE_DTOG_TX(R) (R | USB_EPnR_DTOG_TX)
+#define KEEP_DTOG_TX(R) (R & (~USB_EPnR_DTOG_TX))
+#define SET_VALID_RX(R) ((R & USB_EPnR_STAT_RX) ^ USB_EPnR_STAT_RX) | (R & (~USB_EPnR_STAT_RX))
+#define SET_NAK_RX(R) ((R & USB_EPnR_STAT_RX) ^ USB_EPnR_STAT_RX_1) | (R & (~USB_EPnR_STAT_RX))
+#define SET_STALL_RX(R) ((R & USB_EPnR_STAT_RX) ^ USB_EPnR_STAT_RX_0) | (R & (~USB_EPnR_STAT_RX))
+#define KEEP_STAT_RX(R) (R & (~USB_EPnR_STAT_RX))
+#define SET_VALID_TX(R) ((R & USB_EPnR_STAT_TX) ^ USB_EPnR_STAT_TX) | (R & (~USB_EPnR_STAT_TX))
+#define SET_NAK_TX(R) ((R & USB_EPnR_STAT_TX) ^ USB_EPnR_STAT_TX_1) | (R & (~USB_EPnR_STAT_TX))
+#define SET_STALL_TX(R) ((R & USB_EPnR_STAT_TX) ^ USB_EPnR_STAT_TX_0) | (R & (~USB_EPnR_STAT_TX))
+#define KEEP_STAT_TX(R) (R & (~USB_EPnR_STAT_TX))
+#define CLEAR_CTR_RX(R) (R & (~USB_EPnR_CTR_RX))
+#define CLEAR_CTR_TX(R) (R & (~USB_EPnR_CTR_TX))
+#define CLEAR_CTR_RX_TX(R) (R & (~(USB_EPnR_CTR_TX | USB_EPnR_CTR_RX)))
+
+// USB state: uninitialized, addressed, ready for use
+#define USB_DEFAULT_STATE 0
+#define USB_ADRESSED_STATE 1
+#define USB_CONFIGURE_STATE 2
+
+// EP types
+#define EP_TYPE_BULK 0x00
+#define EP_TYPE_CONTROL 0x01
+#define EP_TYPE_ISO 0x02
+#define EP_TYPE_INTERRUPT 0x03
+
+#define LANG_US (uint16_t)0x0409
+
+#define _USB_STRING_(name, str) \
+static const struct name \
+{ \
+ uint8_t bLength; \
+ uint8_t bDescriptorType; \
+ uint16_t bString[(sizeof(str) - 2) / 2]; \
+ \
+} \
+name = {sizeof(name), 0x03, str}
+
+#define _USB_LANG_ID_(name, lng_id) \
+ \
+static const struct name \
+{ \
+ uint8_t bLength; \
+ uint8_t bDescriptorType; \
+ uint16_t bString; \
+ \
+} \
+name = {0x04, 0x03, lng_id}
+#define STRING_LANG_DESCRIPTOR_SIZE_BYTE (4)
+
+// EP0 configuration packet
+typedef struct {
+ uint8_t bmRequestType;
+ uint8_t bRequest;
+ uint16_t wValue;
+ uint16_t wIndex;
+ uint16_t wLength;
+} config_pack_t;
+
+// endpoints state
+typedef struct __ep_t{
+ uint16_t *tx_buf; // transmission buffer address
+ uint8_t *rx_buf; // reception buffer address
+ uint16_t (*func)(); // endpoint action function
+ uint16_t status; // status flags
+ unsigned rx_cnt : 10; // received data counter
+ unsigned tx_flag : 1; // transmission flag
+ unsigned rx_flag : 1; // reception flag
+ unsigned setup_flag : 1; // this is setup packet (only for EP0)
+} ep_t;
+
+// USB status & its address
+typedef struct {
+ uint8_t USB_Status;
+ uint16_t USB_Addr;
+}usb_dev_t;
+
+typedef struct {
+ uint32_t dwDTERate;
+ uint8_t bCharFormat;
+ #define USB_CDC_1_STOP_BITS 0
+ #define USB_CDC_1_5_STOP_BITS 1
+ #define USB_CDC_2_STOP_BITS 2
+ uint8_t bParityType;
+ #define USB_CDC_NO_PARITY 0
+ #define USB_CDC_ODD_PARITY 1
+ #define USB_CDC_EVEN_PARITY 2
+ #define USB_CDC_MARK_PARITY 3
+ #define USB_CDC_SPACE_PARITY 4
+ uint8_t bDataBits;
+} __attribute__ ((packed)) usb_LineCoding;
+
+typedef struct {
+ uint8_t bmRequestType;
+ uint8_t bNotificationType;
+ uint16_t wValue;
+ uint16_t wIndex;
+ uint16_t wLength;
+} __attribute__ ((packed)) usb_cdc_notification;
+
+extern ep_t endpoints[];
+
+void USB_Init();
+uint8_t USB_GetState();
+int EP_Init(uint8_t number, uint8_t type, uint16_t txsz, uint16_t rxsz, uint16_t (*func)(ep_t ep));
+void EP_WriteIRQ(uint8_t number, const uint8_t *buf, uint16_t size);
+void EP_Write(uint8_t number, const uint8_t *buf, uint16_t size);
+int EP_Read(uint8_t number, uint8_t *buf);
+usb_LineCoding getLineCoding();
+
+
+void WEAK linecoding_handler(usb_LineCoding *lc);
+void WEAK clstate_handler(uint16_t val);
+void WEAK break_handler();
+void WEAK vendor_handler(config_pack_t *packet);
+
+#endif // __USB_LIB_H__
diff --git a/F0:F030,F042,F072/TSYS_controller/version.inc b/F0:F030,F042,F072/TSYS_controller/version.inc
new file mode 100644
index 0000000..c467760
--- /dev/null
+++ b/F0:F030,F042,F072/TSYS_controller/version.inc
@@ -0,0 +1,3 @@
+#define BUILD_NUMBER "48"
+#define BUILD_DATE "2023-08-29"
+#define BUILDNO 45